blob: a9017915ac74b1499e5271a84b7f0ce3b98a1247 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/edk/system/raw_channel.h"
#include <stdint.h>
#include <stdio.h>
#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/rand_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/test_io_thread.h"
#include "base/threading/simple_thread.h"
#include "build/build_config.h" // TODO(vtl): Remove this.
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/platform_handle.h"
#include "mojo/edk/embedder/scoped_platform_handle.h"
#include "mojo/edk/system/message_in_transit.h"
#include "mojo/edk/system/mutex.h"
#include "mojo/edk/system/test_utils.h"
#include "mojo/edk/system/transport_data.h"
#include "mojo/edk/test/test_utils.h"
#include "mojo/public/cpp/system/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace system {
namespace {
scoped_ptr<MessageInTransit> MakeTestMessage(uint32_t num_bytes) {
std::vector<unsigned char> bytes(num_bytes, 0);
for (size_t i = 0; i < num_bytes; i++)
bytes[i] = static_cast<unsigned char>(i + num_bytes);
return make_scoped_ptr(
new MessageInTransit(MessageInTransit::Type::ENDPOINT_CLIENT,
MessageInTransit::Subtype::ENDPOINT_CLIENT_DATA,
num_bytes, bytes.empty() ? nullptr : &bytes[0]));
}
bool CheckMessageData(const void* bytes, uint32_t num_bytes) {
const unsigned char* b = static_cast<const unsigned char*>(bytes);
for (uint32_t i = 0; i < num_bytes; i++) {
if (b[i] != static_cast<unsigned char>(i + num_bytes))
return false;
}
return true;
}
void InitOnIOThread(RawChannel* raw_channel, RawChannel::Delegate* delegate) {
raw_channel->Init(delegate);
}
bool WriteTestMessageToHandle(const embedder::PlatformHandle& handle,
uint32_t num_bytes) {
scoped_ptr<MessageInTransit> message(MakeTestMessage(num_bytes));
size_t write_size = 0;
mojo::test::BlockingWrite(handle, message->main_buffer(),
message->main_buffer_size(), &write_size);
return write_size == message->main_buffer_size();
}
// -----------------------------------------------------------------------------
class RawChannelTest : public testing::Test {
public:
RawChannelTest() : io_thread_(base::TestIOThread::kManualStart) {}
~RawChannelTest() override {}
void SetUp() override {
embedder::PlatformChannelPair channel_pair;
handles[0] = channel_pair.PassServerHandle();
handles[1] = channel_pair.PassClientHandle();
io_thread_.Start();
}
void TearDown() override {
io_thread_.Stop();
handles[0].reset();
handles[1].reset();
}
protected:
base::TestIOThread* io_thread() { return &io_thread_; }
embedder::ScopedPlatformHandle handles[2];
private:
base::TestIOThread io_thread_;
MOJO_DISALLOW_COPY_AND_ASSIGN(RawChannelTest);
};
// RawChannelTest.WriteMessage -------------------------------------------------
class WriteOnlyRawChannelDelegate : public RawChannel::Delegate {
public:
WriteOnlyRawChannelDelegate() {}
~WriteOnlyRawChannelDelegate() override {}
// |RawChannel::Delegate| implementation:
void OnReadMessage(
const MessageInTransit::View& /*message_view*/,
embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) override {
CHECK(false); // Should not get called.
}
void OnError(Error error) override {
// We'll get a read (shutdown) error when the connection is closed.
CHECK_EQ(error, ERROR_READ_SHUTDOWN);
}
private:
MOJO_DISALLOW_COPY_AND_ASSIGN(WriteOnlyRawChannelDelegate);
};
static const unsigned kMessageReaderSleepMs = 1;
static const size_t kMessageReaderMaxPollIterations = 3000;
class TestMessageReaderAndChecker {
public:
explicit TestMessageReaderAndChecker(embedder::PlatformHandle handle)
: handle_(handle) {}
~TestMessageReaderAndChecker() { CHECK(bytes_.empty()); }
bool ReadAndCheckNextMessage(uint32_t expected_size) {
unsigned char buffer[4096];
for (size_t i = 0; i < kMessageReaderMaxPollIterations;) {
size_t read_size = 0;
CHECK(mojo::test::NonBlockingRead(handle_, buffer, sizeof(buffer),
&read_size));
// Append newly-read data to |bytes_|.
bytes_.insert(bytes_.end(), buffer, buffer + read_size);
// If we have the header....
size_t message_size;
if (MessageInTransit::GetNextMessageSize(
bytes_.empty() ? nullptr : &bytes_[0], bytes_.size(),
&message_size)) {
// If we've read the whole message....
if (bytes_.size() >= message_size) {
bool rv = true;
MessageInTransit::View message_view(message_size, &bytes_[0]);
CHECK_EQ(message_view.main_buffer_size(), message_size);
if (message_view.num_bytes() != expected_size) {
LOG(ERROR) << "Wrong size: " << message_size << " instead of "
<< expected_size << " bytes.";
rv = false;
} else if (!CheckMessageData(message_view.bytes(),
message_view.num_bytes())) {
LOG(ERROR) << "Incorrect message bytes.";
rv = false;
}
// Erase message data.
bytes_.erase(bytes_.begin(),
bytes_.begin() + message_view.main_buffer_size());
return rv;
}
}
if (static_cast<size_t>(read_size) < sizeof(buffer)) {
i++;
test::Sleep(test::DeadlineFromMilliseconds(kMessageReaderSleepMs));
}
}
LOG(ERROR) << "Too many iterations.";
return false;
}
private:
const embedder::PlatformHandle handle_;
// The start of the received data should always be on a message boundary.
std::vector<unsigned char> bytes_;
MOJO_DISALLOW_COPY_AND_ASSIGN(TestMessageReaderAndChecker);
};
// Tests writing (and verifies reading using our own custom reader).
TEST_F(RawChannelTest, WriteMessage) {
WriteOnlyRawChannelDelegate delegate;
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
TestMessageReaderAndChecker checker(handles[1].get());
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
// Write and read, for a variety of sizes.
for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) {
EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size)));
EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size;
}
// Write/queue and read afterwards, for a variety of sizes.
for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
EXPECT_TRUE(rc->WriteMessage(MakeTestMessage(size)));
for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
EXPECT_TRUE(checker.ReadAndCheckNextMessage(size)) << size;
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
}
// RawChannelTest.OnReadMessage ------------------------------------------------
class ReadCheckerRawChannelDelegate : public RawChannel::Delegate {
public:
ReadCheckerRawChannelDelegate() : done_event_(false, false), position_(0) {}
~ReadCheckerRawChannelDelegate() override {}
// |RawChannel::Delegate| implementation (called on the I/O thread):
void OnReadMessage(
const MessageInTransit::View& message_view,
embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
EXPECT_FALSE(platform_handles);
size_t position;
size_t expected_size;
bool should_signal = false;
{
MutexLocker locker(&mutex_);
CHECK_LT(position_, expected_sizes_.size());
position = position_;
expected_size = expected_sizes_[position];
position_++;
if (position_ >= expected_sizes_.size())
should_signal = true;
}
EXPECT_EQ(expected_size, message_view.num_bytes()) << position;
if (message_view.num_bytes() == expected_size) {
EXPECT_TRUE(
CheckMessageData(message_view.bytes(), message_view.num_bytes()))
<< position;
}
if (should_signal)
done_event_.Signal();
}
void OnError(Error error) override {
// We'll get a read (shutdown) error when the connection is closed.
CHECK_EQ(error, ERROR_READ_SHUTDOWN);
}
// Waits for all the messages (of sizes |expected_sizes_|) to be seen.
void Wait() { done_event_.Wait(); }
void SetExpectedSizes(const std::vector<uint32_t>& expected_sizes) {
MutexLocker locker(&mutex_);
CHECK_EQ(position_, expected_sizes_.size());
expected_sizes_ = expected_sizes;
position_ = 0;
}
private:
base::WaitableEvent done_event_;
Mutex mutex_;
std::vector<uint32_t> expected_sizes_ MOJO_GUARDED_BY(mutex_);
size_t position_ MOJO_GUARDED_BY(mutex_);
MOJO_DISALLOW_COPY_AND_ASSIGN(ReadCheckerRawChannelDelegate);
};
// Tests reading (writing using our own custom writer).
TEST_F(RawChannelTest, OnReadMessage) {
ReadCheckerRawChannelDelegate delegate;
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
// Write and read, for a variety of sizes.
for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1) {
delegate.SetExpectedSizes(std::vector<uint32_t>(1, size));
EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size));
delegate.Wait();
}
// Set up reader and write as fast as we can.
// Write/queue and read afterwards, for a variety of sizes.
std::vector<uint32_t> expected_sizes;
for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
expected_sizes.push_back(size);
delegate.SetExpectedSizes(expected_sizes);
for (uint32_t size = 1; size < 5 * 1000 * 1000; size += size / 2 + 1)
EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), size));
delegate.Wait();
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
}
// RawChannelTest.WriteMessageAndOnReadMessage ---------------------------------
class RawChannelWriterThread : public base::SimpleThread {
public:
RawChannelWriterThread(RawChannel* raw_channel, size_t write_count)
: base::SimpleThread("raw_channel_writer_thread"),
raw_channel_(raw_channel),
left_to_write_(write_count) {}
~RawChannelWriterThread() override { Join(); }
private:
void Run() override {
static const int kMaxRandomMessageSize = 25000;
while (left_to_write_-- > 0) {
EXPECT_TRUE(raw_channel_->WriteMessage(MakeTestMessage(
static_cast<uint32_t>(base::RandInt(1, kMaxRandomMessageSize)))));
}
}
RawChannel* const raw_channel_;
size_t left_to_write_;
MOJO_DISALLOW_COPY_AND_ASSIGN(RawChannelWriterThread);
};
class ReadCountdownRawChannelDelegate : public RawChannel::Delegate {
public:
explicit ReadCountdownRawChannelDelegate(size_t expected_count)
: done_event_(false, false), expected_count_(expected_count), count_(0) {}
~ReadCountdownRawChannelDelegate() override {}
// |RawChannel::Delegate| implementation (called on the I/O thread):
void OnReadMessage(
const MessageInTransit::View& message_view,
embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
EXPECT_FALSE(platform_handles);
EXPECT_LT(count_, expected_count_);
count_++;
EXPECT_TRUE(
CheckMessageData(message_view.bytes(), message_view.num_bytes()));
if (count_ >= expected_count_)
done_event_.Signal();
}
void OnError(Error error) override {
// We'll get a read (shutdown) error when the connection is closed.
CHECK_EQ(error, ERROR_READ_SHUTDOWN);
}
// Waits for all the messages to have been seen.
void Wait() { done_event_.Wait(); }
private:
base::WaitableEvent done_event_;
size_t expected_count_;
size_t count_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ReadCountdownRawChannelDelegate);
};
TEST_F(RawChannelTest, WriteMessageAndOnReadMessage) {
static const size_t kNumWriterThreads = 10;
static const size_t kNumWriteMessagesPerThread = 4000;
WriteOnlyRawChannelDelegate writer_delegate;
scoped_ptr<RawChannel> writer_rc(RawChannel::Create(handles[0].Pass()));
io_thread()->PostTaskAndWait(FROM_HERE,
base::Bind(&InitOnIOThread, writer_rc.get(),
base::Unretained(&writer_delegate)));
ReadCountdownRawChannelDelegate reader_delegate(kNumWriterThreads *
kNumWriteMessagesPerThread);
scoped_ptr<RawChannel> reader_rc(RawChannel::Create(handles[1].Pass()));
io_thread()->PostTaskAndWait(FROM_HERE,
base::Bind(&InitOnIOThread, reader_rc.get(),
base::Unretained(&reader_delegate)));
{
ScopedVector<RawChannelWriterThread> writer_threads;
for (size_t i = 0; i < kNumWriterThreads; i++) {
writer_threads.push_back(new RawChannelWriterThread(
writer_rc.get(), kNumWriteMessagesPerThread));
}
for (size_t i = 0; i < writer_threads.size(); i++)
writer_threads[i]->Start();
} // Joins all the writer threads.
// Sleep a bit, to let any extraneous reads be processed. (There shouldn't be
// any, but we want to know about them.)
test::Sleep(test::DeadlineFromMilliseconds(100));
// Wait for reading to finish.
reader_delegate.Wait();
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&RawChannel::Shutdown, base::Unretained(reader_rc.get())));
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&RawChannel::Shutdown, base::Unretained(writer_rc.get())));
}
// RawChannelTest.OnError ------------------------------------------------------
class ErrorRecordingRawChannelDelegate
: public ReadCountdownRawChannelDelegate {
public:
ErrorRecordingRawChannelDelegate(size_t expected_read_count,
bool expect_read_error,
bool expect_write_error)
: ReadCountdownRawChannelDelegate(expected_read_count),
got_read_error_event_(false, false),
got_write_error_event_(false, false),
expecting_read_error_(expect_read_error),
expecting_write_error_(expect_write_error) {}
~ErrorRecordingRawChannelDelegate() override {}
void OnError(Error error) override {
switch (error) {
case ERROR_READ_SHUTDOWN:
ASSERT_TRUE(expecting_read_error_);
expecting_read_error_ = false;
got_read_error_event_.Signal();
break;
case ERROR_READ_BROKEN:
// TODO(vtl): Test broken connections.
CHECK(false);
break;
case ERROR_READ_BAD_MESSAGE:
// TODO(vtl): Test reception/detection of bad messages.
CHECK(false);
break;
case ERROR_READ_UNKNOWN:
// TODO(vtl): Test however it is we might get here.
CHECK(false);
break;
case ERROR_WRITE:
ASSERT_TRUE(expecting_write_error_);
expecting_write_error_ = false;
got_write_error_event_.Signal();
break;
}
}
void WaitForReadError() { got_read_error_event_.Wait(); }
void WaitForWriteError() { got_write_error_event_.Wait(); }
private:
base::WaitableEvent got_read_error_event_;
base::WaitableEvent got_write_error_event_;
bool expecting_read_error_;
bool expecting_write_error_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ErrorRecordingRawChannelDelegate);
};
// Tests (fatal) errors.
TEST_F(RawChannelTest, OnError) {
ErrorRecordingRawChannelDelegate delegate(0, true, true);
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
// Close the handle of the other end, which should make writing fail.
handles[1].reset();
EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
// We should get a write error.
delegate.WaitForWriteError();
// We should also get a read error.
delegate.WaitForReadError();
EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(2)));
// Sleep a bit, to make sure we don't get another |OnError()|
// notification. (If we actually get another one, |OnError()| crashes.)
test::Sleep(test::DeadlineFromMilliseconds(20));
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
}
// RawChannelTest.ReadUnaffectedByWriteError -----------------------------------
TEST_F(RawChannelTest, ReadUnaffectedByWriteError) {
const size_t kMessageCount = 5;
// Write a few messages into the other end.
uint32_t message_size = 1;
for (size_t i = 0; i < kMessageCount;
i++, message_size += message_size / 2 + 1)
EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), message_size));
// Close the other end, which should make writing fail.
handles[1].reset();
// Only start up reading here. The system buffer should still contain the
// messages that were written.
ErrorRecordingRawChannelDelegate delegate(kMessageCount, true, true);
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
// We should definitely get a write error.
delegate.WaitForWriteError();
// Wait for reading to finish. A writing failure shouldn't affect reading.
delegate.Wait();
// And then we should get a read error.
delegate.WaitForReadError();
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
}
// RawChannelTest.WriteMessageAfterShutdown ------------------------------------
// Makes sure that calling |WriteMessage()| after |Shutdown()| behaves
// correctly.
TEST_F(RawChannelTest, WriteMessageAfterShutdown) {
WriteOnlyRawChannelDelegate delegate;
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get())));
EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
}
// RawChannelTest.{Shutdown, ShutdownAndDestroy}OnReadMessage ------------------
class ShutdownOnReadMessageRawChannelDelegate : public RawChannel::Delegate {
public:
explicit ShutdownOnReadMessageRawChannelDelegate(RawChannel* raw_channel,
bool should_destroy)
: raw_channel_(raw_channel),
should_destroy_(should_destroy),
done_event_(false, false),
did_shutdown_(false) {}
~ShutdownOnReadMessageRawChannelDelegate() override {}
// |RawChannel::Delegate| implementation (called on the I/O thread):
void OnReadMessage(
const MessageInTransit::View& message_view,
embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
EXPECT_FALSE(platform_handles);
EXPECT_FALSE(did_shutdown_);
EXPECT_TRUE(
CheckMessageData(message_view.bytes(), message_view.num_bytes()));
raw_channel_->Shutdown();
if (should_destroy_)
delete raw_channel_;
did_shutdown_ = true;
done_event_.Signal();
}
void OnError(Error /*error*/) override {
CHECK(false); // Should not get called.
}
// Waits for shutdown.
void Wait() {
done_event_.Wait();
EXPECT_TRUE(did_shutdown_);
}
private:
RawChannel* const raw_channel_;
const bool should_destroy_;
base::WaitableEvent done_event_;
bool did_shutdown_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ShutdownOnReadMessageRawChannelDelegate);
};
TEST_F(RawChannelTest, ShutdownOnReadMessage) {
// Write a few messages into the other end.
for (size_t count = 0; count < 5; count++)
EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), 10));
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
ShutdownOnReadMessageRawChannelDelegate delegate(rc.get(), false);
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
// Wait for the delegate, which will shut the |RawChannel| down.
delegate.Wait();
}
TEST_F(RawChannelTest, ShutdownAndDestroyOnReadMessage) {
// Write a message into the other end.
EXPECT_TRUE(WriteTestMessageToHandle(handles[1].get(), 10));
// The delegate will destroy |rc|.
RawChannel* rc = RawChannel::Create(handles[0].Pass()).release();
ShutdownOnReadMessageRawChannelDelegate delegate(rc, true);
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&InitOnIOThread, rc, base::Unretained(&delegate)));
// Wait for the delegate, which will shut the |RawChannel| down.
delegate.Wait();
}
// RawChannelTest.{Shutdown, ShutdownAndDestroy}OnError{Read, Write} -----------
class ShutdownOnErrorRawChannelDelegate : public RawChannel::Delegate {
public:
ShutdownOnErrorRawChannelDelegate(RawChannel* raw_channel,
bool should_destroy,
Error shutdown_on_error_type)
: raw_channel_(raw_channel),
should_destroy_(should_destroy),
shutdown_on_error_type_(shutdown_on_error_type),
done_event_(false, false),
did_shutdown_(false) {}
~ShutdownOnErrorRawChannelDelegate() override {}
// |RawChannel::Delegate| implementation (called on the I/O thread):
void OnReadMessage(
const MessageInTransit::View& /*message_view*/,
embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) override {
CHECK(false); // Should not get called.
}
void OnError(Error error) override {
EXPECT_FALSE(did_shutdown_);
if (error != shutdown_on_error_type_)
return;
raw_channel_->Shutdown();
if (should_destroy_)
delete raw_channel_;
did_shutdown_ = true;
done_event_.Signal();
}
// Waits for shutdown.
void Wait() {
done_event_.Wait();
EXPECT_TRUE(did_shutdown_);
}
private:
RawChannel* const raw_channel_;
const bool should_destroy_;
const Error shutdown_on_error_type_;
base::WaitableEvent done_event_;
bool did_shutdown_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ShutdownOnErrorRawChannelDelegate);
};
TEST_F(RawChannelTest, ShutdownOnErrorRead) {
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
ShutdownOnErrorRawChannelDelegate delegate(
rc.get(), false, RawChannel::Delegate::ERROR_READ_SHUTDOWN);
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
// Close the handle of the other end, which should stuff fail.
handles[1].reset();
// Wait for the delegate, which will shut the |RawChannel| down.
delegate.Wait();
}
TEST_F(RawChannelTest, ShutdownAndDestroyOnErrorRead) {
RawChannel* rc = RawChannel::Create(handles[0].Pass()).release();
ShutdownOnErrorRawChannelDelegate delegate(
rc, true, RawChannel::Delegate::ERROR_READ_SHUTDOWN);
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&InitOnIOThread, rc, base::Unretained(&delegate)));
// Close the handle of the other end, which should stuff fail.
handles[1].reset();
// Wait for the delegate, which will shut the |RawChannel| down.
delegate.Wait();
}
TEST_F(RawChannelTest, ShutdownOnErrorWrite) {
scoped_ptr<RawChannel> rc(RawChannel::Create(handles[0].Pass()));
ShutdownOnErrorRawChannelDelegate delegate(rc.get(), false,
RawChannel::Delegate::ERROR_WRITE);
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate)));
// Close the handle of the other end, which should stuff fail.
handles[1].reset();
EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
// Wait for the delegate, which will shut the |RawChannel| down.
delegate.Wait();
}
TEST_F(RawChannelTest, ShutdownAndDestroyOnErrorWrite) {
RawChannel* rc = RawChannel::Create(handles[0].Pass()).release();
ShutdownOnErrorRawChannelDelegate delegate(rc, true,
RawChannel::Delegate::ERROR_WRITE);
io_thread()->PostTaskAndWait(
FROM_HERE, base::Bind(&InitOnIOThread, rc, base::Unretained(&delegate)));
// Close the handle of the other end, which should stuff fail.
handles[1].reset();
EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1)));
// Wait for the delegate, which will shut the |RawChannel| down.
delegate.Wait();
}
// RawChannelTest.ReadWritePlatformHandles -------------------------------------
class ReadPlatformHandlesCheckerRawChannelDelegate
: public RawChannel::Delegate {
public:
ReadPlatformHandlesCheckerRawChannelDelegate() : done_event_(false, false) {}
~ReadPlatformHandlesCheckerRawChannelDelegate() override {}
// |RawChannel::Delegate| implementation (called on the I/O thread):
void OnReadMessage(
const MessageInTransit::View& message_view,
embedder::ScopedPlatformHandleVectorPtr platform_handles) override {
const char kHello[] = "hello";
EXPECT_EQ(sizeof(kHello), message_view.num_bytes());
EXPECT_STREQ(kHello, static_cast<const char*>(message_view.bytes()));
ASSERT_TRUE(platform_handles);
ASSERT_EQ(2u, platform_handles->size());
embedder::ScopedPlatformHandle h1(platform_handles->at(0));
EXPECT_TRUE(h1.is_valid());
embedder::ScopedPlatformHandle h2(platform_handles->at(1));
EXPECT_TRUE(h2.is_valid());
platform_handles->clear();
{
char buffer[100] = {};
base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h1.Pass(), "rb"));
EXPECT_TRUE(fp);
rewind(fp.get());
EXPECT_EQ(1u, fread(buffer, 1, sizeof(buffer), fp.get()));
EXPECT_EQ('1', buffer[0]);
}
{
char buffer[100] = {};
base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h2.Pass(), "rb"));
EXPECT_TRUE(fp);
rewind(fp.get());
EXPECT_EQ(1u, fread(buffer, 1, sizeof(buffer), fp.get()));
EXPECT_EQ('2', buffer[0]);
}
done_event_.Signal();
}
void OnError(Error error) override {
// We'll get a read (shutdown) error when the connection is closed.
CHECK_EQ(error, ERROR_READ_SHUTDOWN);
}
void Wait() { done_event_.Wait(); }
private:
base::WaitableEvent done_event_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ReadPlatformHandlesCheckerRawChannelDelegate);
};
#if defined(OS_POSIX)
#define MAYBE_ReadWritePlatformHandles ReadWritePlatformHandles
#else
// Not yet implemented (on Windows).
#define MAYBE_ReadWritePlatformHandles DISABLED_ReadWritePlatformHandles
#endif
TEST_F(RawChannelTest, MAYBE_ReadWritePlatformHandles) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
WriteOnlyRawChannelDelegate write_delegate;
scoped_ptr<RawChannel> rc_write(RawChannel::Create(handles[0].Pass()));
io_thread()->PostTaskAndWait(FROM_HERE,
base::Bind(&InitOnIOThread, rc_write.get(),
base::Unretained(&write_delegate)));
ReadPlatformHandlesCheckerRawChannelDelegate read_delegate;
scoped_ptr<RawChannel> rc_read(RawChannel::Create(handles[1].Pass()));
io_thread()->PostTaskAndWait(FROM_HERE,
base::Bind(&InitOnIOThread, rc_read.get(),
base::Unretained(&read_delegate)));
base::FilePath unused;
base::ScopedFILE fp1(
base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
EXPECT_EQ(1u, fwrite("1", 1, 1, fp1.get()));
base::ScopedFILE fp2(
base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
EXPECT_EQ(1u, fwrite("2", 1, 1, fp2.get()));
{
const char kHello[] = "hello";
embedder::ScopedPlatformHandleVectorPtr platform_handles(
new embedder::PlatformHandleVector());
platform_handles->push_back(
mojo::test::PlatformHandleFromFILE(fp1.Pass()).release());
platform_handles->push_back(
mojo::test::PlatformHandleFromFILE(fp2.Pass()).release());
scoped_ptr<MessageInTransit> message(
new MessageInTransit(MessageInTransit::Type::ENDPOINT_CLIENT,
MessageInTransit::Subtype::ENDPOINT_CLIENT_DATA,
sizeof(kHello), kHello));
message->SetTransportData(make_scoped_ptr(new TransportData(
platform_handles.Pass(), rc_write->GetSerializedPlatformHandleSize())));
EXPECT_TRUE(rc_write->WriteMessage(message.Pass()));
}
read_delegate.Wait();
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&RawChannel::Shutdown, base::Unretained(rc_read.get())));
io_thread()->PostTaskAndWait(
FROM_HERE,
base::Bind(&RawChannel::Shutdown, base::Unretained(rc_write.get())));
}
} // namespace
} // namespace system
} // namespace mojo