blob: 89f17cb0eef8384da9b825a48331b1299f550de6 [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 <stdlib.h>
#include <string.h>
#include "mojo/public/cpp/bindings/lib/message_builder.h"
#include "mojo/public/cpp/bindings/lib/message_queue.h"
#include "mojo/public/cpp/bindings/lib/router.h"
#include "mojo/public/cpp/environment/environment.h"
#include "mojo/public/cpp/system/macros.h"
#include "mojo/public/cpp/utility/run_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace test {
namespace {
void AllocRequestMessage(uint32_t name, const char* text, Message* message) {
size_t payload_size = strlen(text) + 1; // Plus null terminator.
internal::RequestMessageBuilder builder(name, payload_size);
memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
builder.Finish(message);
}
void AllocResponseMessage(uint32_t name, const char* text,
uint64_t request_id, Message* message) {
size_t payload_size = strlen(text) + 1; // Plus null terminator.
internal::ResponseMessageBuilder builder(name, payload_size, request_id);
memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
builder.Finish(message);
}
class MessageAccumulator : public MessageReceiver {
public:
explicit MessageAccumulator(internal::MessageQueue* queue) : queue_(queue) {
}
virtual bool Accept(Message* message) MOJO_OVERRIDE {
queue_->Push(message);
return true;
}
private:
internal::MessageQueue* queue_;
};
class ResponseGenerator : public MessageReceiverWithResponder {
public:
ResponseGenerator() {
}
virtual bool Accept(Message* message) MOJO_OVERRIDE {
return false;
}
virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder)
MOJO_OVERRIDE {
EXPECT_TRUE(message->has_flag(internal::kMessageExpectsResponse));
return SendResponse(message->name(), message->request_id(), responder);
}
bool SendResponse(uint32_t name, uint64_t request_id,
MessageReceiver* responder) {
Message response;
AllocResponseMessage(name, "world", request_id, &response);
bool result = responder->Accept(&response);
delete responder;
return result;
}
};
class LazyResponseGenerator : public ResponseGenerator {
public:
LazyResponseGenerator() : responder_(NULL), name_(0), request_id_(0) {
}
virtual ~LazyResponseGenerator() {
delete responder_;
}
virtual bool AcceptWithResponder(Message* message, MessageReceiver* responder)
MOJO_OVERRIDE {
name_ = message->name();
request_id_ = message->request_id();
responder_ = responder;
return true;
}
bool has_responder() const { return !!responder_; }
void Complete() {
SendResponse(name_, request_id_, responder_);
responder_ = NULL;
}
private:
MessageReceiver* responder_;
uint32_t name_;
uint32_t request_id_;
};
class RouterTest : public testing::Test {
public:
RouterTest() {
}
virtual void SetUp() MOJO_OVERRIDE {
CreateMessagePipe(NULL, &handle0_, &handle1_);
}
virtual void TearDown() MOJO_OVERRIDE {
}
void PumpMessages() {
loop_.RunUntilIdle();
}
protected:
ScopedMessagePipeHandle handle0_;
ScopedMessagePipeHandle handle1_;
private:
Environment env_;
RunLoop loop_;
};
TEST_F(RouterTest, BasicRequestResponse) {
internal::Router router0(handle0_.Pass(), internal::FilterChain());
internal::Router router1(handle1_.Pass(), internal::FilterChain());
ResponseGenerator generator;
router1.set_incoming_receiver(&generator);
Message request;
AllocRequestMessage(1, "hello", &request);
internal::MessageQueue message_queue;
router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
PumpMessages();
EXPECT_FALSE(message_queue.IsEmpty());
Message response;
message_queue.Pop(&response);
EXPECT_EQ(std::string("world"),
std::string(reinterpret_cast<const char*>(response.payload())));
}
TEST_F(RouterTest, BasicRequestResponse_Synchronous) {
internal::Router router0(handle0_.Pass(), internal::FilterChain());
internal::Router router1(handle1_.Pass(), internal::FilterChain());
ResponseGenerator generator;
router1.set_incoming_receiver(&generator);
Message request;
AllocRequestMessage(1, "hello", &request);
internal::MessageQueue message_queue;
router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
router1.WaitForIncomingMessage();
router0.WaitForIncomingMessage();
EXPECT_FALSE(message_queue.IsEmpty());
Message response;
message_queue.Pop(&response);
EXPECT_EQ(std::string("world"),
std::string(reinterpret_cast<const char*>(response.payload())));
}
TEST_F(RouterTest, RequestWithNoReceiver) {
internal::Router router0(handle0_.Pass(), internal::FilterChain());
internal::Router router1(handle1_.Pass(), internal::FilterChain());
// Without an incoming receiver set on router1, we expect router0 to observe
// an error as a result of sending a message.
Message request;
AllocRequestMessage(1, "hello", &request);
internal::MessageQueue message_queue;
router0.AcceptWithResponder(&request, new MessageAccumulator(&message_queue));
PumpMessages();
EXPECT_TRUE(router0.encountered_error());
EXPECT_TRUE(router1.encountered_error());
EXPECT_TRUE(message_queue.IsEmpty());
}
TEST_F(RouterTest, LateResponse) {
// Test that things won't blow up if we try to send a message to a
// MessageReceiver, which was given to us via AcceptWithResponder,
// after the router has gone away.
LazyResponseGenerator generator;
{
internal::Router router0(handle0_.Pass(), internal::FilterChain());
internal::Router router1(handle1_.Pass(), internal::FilterChain());
router1.set_incoming_receiver(&generator);
Message request;
AllocRequestMessage(1, "hello", &request);
internal::MessageQueue message_queue;
router0.AcceptWithResponder(&request,
new MessageAccumulator(&message_queue));
PumpMessages();
EXPECT_TRUE(generator.has_responder());
}
generator.Complete(); // This should end up doing nothing.
}
} // namespace
} // namespace test
} // namespace mojo