blob: a20190e8a3c835006dec257805fd68a1d2010297 [file] [log] [blame]
// Copyright 2013 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 <stdlib.h>
#include <string.h>
#include "mojo/public/bindings/lib/bindings_support.h"
#include "mojo/public/bindings/lib/connector.h"
#include "mojo/public/bindings/lib/message_queue.h"
#include "mojo/public/system/macros.h"
#include "mojo/public/tests/simple_bindings_support.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
class MessageAccumulator : public MessageReceiver {
public:
MessageAccumulator() {
}
virtual bool Accept(Message* message) MOJO_OVERRIDE {
queue_.Push(message);
return true;
}
bool IsEmpty() const {
return queue_.IsEmpty();
}
void Pop(Message* message) {
queue_.Pop(message);
}
private:
MessageQueue queue_;
};
class BindingsConnectorTest : public testing::Test {
public:
BindingsConnectorTest() {
}
virtual void SetUp() MOJO_OVERRIDE {
CreateMessagePipe(&handle0_, &handle1_);
}
virtual void TearDown() MOJO_OVERRIDE {
}
void AllocMessage(const char* text, Message* message) {
size_t payload_size = strlen(text) + 1; // Plus null terminator.
size_t num_bytes = sizeof(MessageHeader) + payload_size;
message->data = static_cast<MessageData*>(malloc(num_bytes));
message->data->header.num_bytes = static_cast<uint32_t>(num_bytes);
message->data->header.name = 1;
memcpy(message->data->payload, text, payload_size);
}
void PumpMessages() {
bindings_support_.Process();
}
protected:
ScopedMessagePipeHandle handle0_;
ScopedMessagePipeHandle handle1_;
private:
SimpleBindingsSupport bindings_support_;
};
TEST_F(BindingsConnectorTest, Basic) {
internal::Connector connector0(handle0_.Pass());
internal::Connector connector1(handle1_.Pass());
const char kText[] = "hello world";
Message message;
AllocMessage(kText, &message);
connector0.Accept(&message);
MessageAccumulator accumulator;
connector1.SetIncomingReceiver(&accumulator);
PumpMessages();
ASSERT_FALSE(accumulator.IsEmpty());
Message message_received;
accumulator.Pop(&message_received);
EXPECT_EQ(std::string(kText),
std::string(
reinterpret_cast<char*>(message_received.data->payload)));
}
TEST_F(BindingsConnectorTest, Basic_EarlyIncomingReceiver) {
internal::Connector connector0(handle0_.Pass());
internal::Connector connector1(handle1_.Pass());
MessageAccumulator accumulator;
connector1.SetIncomingReceiver(&accumulator);
const char kText[] = "hello world";
Message message;
AllocMessage(kText, &message);
connector0.Accept(&message);
PumpMessages();
ASSERT_FALSE(accumulator.IsEmpty());
Message message_received;
accumulator.Pop(&message_received);
EXPECT_EQ(std::string(kText),
std::string(
reinterpret_cast<char*>(message_received.data->payload)));
}
TEST_F(BindingsConnectorTest, Basic_TwoMessages) {
internal::Connector connector0(handle0_.Pass());
internal::Connector connector1(handle1_.Pass());
const char* kText[] = { "hello", "world" };
for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
Message message;
AllocMessage(kText[i], &message);
connector0.Accept(&message);
}
MessageAccumulator accumulator;
connector1.SetIncomingReceiver(&accumulator);
PumpMessages();
for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
ASSERT_FALSE(accumulator.IsEmpty());
Message message_received;
accumulator.Pop(&message_received);
EXPECT_EQ(std::string(kText[i]),
std::string(
reinterpret_cast<char*>(message_received.data->payload)));
}
}
TEST_F(BindingsConnectorTest, WriteToClosedPipe) {
// Leak this, so the closed handle isn't closed again.
MojoHandle mojo_handle = handle0_.get().value();
internal::Connector* connector0 = new internal::Connector(handle0_.Pass());
const char kText[] = "hello world";
Message message;
AllocMessage(kText, &message);
// Close handle out from under the connection
MojoClose(mojo_handle);
bool ok = connector0->Accept(&message);
EXPECT_FALSE(ok);
EXPECT_TRUE(connector0->encountered_error());
}
// Enable this test once MojoWriteMessage supports passing handles.
TEST_F(BindingsConnectorTest, MessageWithHandles) {
internal::Connector connector0(handle0_.Pass());
internal::Connector connector1(handle1_.Pass());
const char kText[] = "hello world";
Message message;
AllocMessage(kText, &message);
ScopedMessagePipeHandle handles[2];
CreateMessagePipe(&handles[0], &handles[1]);
message.handles.push_back(handles[0].release());
connector0.Accept(&message);
// The message should have been transferred, releasing the handles.
EXPECT_TRUE(message.handles.empty());
MessageAccumulator accumulator;
connector1.SetIncomingReceiver(&accumulator);
PumpMessages();
ASSERT_FALSE(accumulator.IsEmpty());
Message message_received;
accumulator.Pop(&message_received);
EXPECT_EQ(std::string(kText),
std::string(
reinterpret_cast<char*>(message_received.data->payload)));
ASSERT_EQ(1U, message_received.handles.size());
// Now send a message to the transferred handle and confirm it's sent through
// to the orginal pipe.
// TODO(vtl): Do we need a better way of "downcasting" the handle types?
ScopedMessagePipeHandle smph;
smph.reset(MessagePipeHandle(message_received.handles[0].value()));
message_received.handles[0] = Handle(); // |smph| now owns this handle.
internal::Connector connector_received(smph.Pass());
internal::Connector connector_original(handles[1].Pass());
AllocMessage(kText, &message);
connector_received.Accept(&message);
connector_original.SetIncomingReceiver(&accumulator);
PumpMessages();
ASSERT_FALSE(accumulator.IsEmpty());
accumulator.Pop(&message_received);
EXPECT_EQ(std::string(kText),
std::string(
reinterpret_cast<char*>(message_received.data->payload)));
}
} // namespace test
} // namespace mojo