1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "mojo/public/cpp/bindings/lib/connector.h"
9 #include "mojo/public/cpp/bindings/lib/message_builder.h"
10 #include "mojo/public/cpp/bindings/lib/message_queue.h"
11 #include "mojo/public/cpp/environment/environment.h"
12 #include "mojo/public/cpp/system/macros.h"
13 #include "mojo/public/cpp/utility/run_loop.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 namespace mojo {
17 namespace test {
18 namespace {
19
20 class MessageAccumulator : public MessageReceiver {
21 public:
MessageAccumulator()22 MessageAccumulator() {
23 }
24
Accept(Message * message)25 virtual bool Accept(Message* message) MOJO_OVERRIDE {
26 queue_.Push(message);
27 return true;
28 }
29
IsEmpty() const30 bool IsEmpty() const {
31 return queue_.IsEmpty();
32 }
33
Pop(Message * message)34 void Pop(Message* message) {
35 queue_.Pop(message);
36 }
37
38 private:
39 internal::MessageQueue queue_;
40 };
41
42 class ConnectorTest : public testing::Test {
43 public:
ConnectorTest()44 ConnectorTest() {
45 }
46
SetUp()47 virtual void SetUp() MOJO_OVERRIDE {
48 CreateMessagePipe(NULL, &handle0_, &handle1_);
49 }
50
TearDown()51 virtual void TearDown() MOJO_OVERRIDE {
52 }
53
AllocMessage(const char * text,Message * message)54 void AllocMessage(const char* text, Message* message) {
55 size_t payload_size = strlen(text) + 1; // Plus null terminator.
56 internal::MessageBuilder builder(1, payload_size);
57 memcpy(builder.buffer()->Allocate(payload_size), text, payload_size);
58 builder.Finish(message);
59 }
60
PumpMessages()61 void PumpMessages() {
62 loop_.RunUntilIdle();
63 }
64
65 protected:
66 ScopedMessagePipeHandle handle0_;
67 ScopedMessagePipeHandle handle1_;
68
69 private:
70 Environment env_;
71 RunLoop loop_;
72 };
73
TEST_F(ConnectorTest,Basic)74 TEST_F(ConnectorTest, Basic) {
75 internal::Connector connector0(handle0_.Pass());
76 internal::Connector connector1(handle1_.Pass());
77
78 const char kText[] = "hello world";
79
80 Message message;
81 AllocMessage(kText, &message);
82
83 connector0.Accept(&message);
84
85 MessageAccumulator accumulator;
86 connector1.set_incoming_receiver(&accumulator);
87
88 PumpMessages();
89
90 ASSERT_FALSE(accumulator.IsEmpty());
91
92 Message message_received;
93 accumulator.Pop(&message_received);
94
95 EXPECT_EQ(
96 std::string(kText),
97 std::string(reinterpret_cast<const char*>(message_received.payload())));
98 }
99
TEST_F(ConnectorTest,Basic_EarlyIncomingReceiver)100 TEST_F(ConnectorTest, Basic_EarlyIncomingReceiver) {
101 internal::Connector connector0(handle0_.Pass());
102 internal::Connector connector1(handle1_.Pass());
103
104 MessageAccumulator accumulator;
105 connector1.set_incoming_receiver(&accumulator);
106
107 const char kText[] = "hello world";
108
109 Message message;
110 AllocMessage(kText, &message);
111
112 connector0.Accept(&message);
113
114 PumpMessages();
115
116 ASSERT_FALSE(accumulator.IsEmpty());
117
118 Message message_received;
119 accumulator.Pop(&message_received);
120
121 EXPECT_EQ(
122 std::string(kText),
123 std::string(reinterpret_cast<const char*>(message_received.payload())));
124 }
125
TEST_F(ConnectorTest,Basic_TwoMessages)126 TEST_F(ConnectorTest, Basic_TwoMessages) {
127 internal::Connector connector0(handle0_.Pass());
128 internal::Connector connector1(handle1_.Pass());
129
130 const char* kText[] = { "hello", "world" };
131
132 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
133 Message message;
134 AllocMessage(kText[i], &message);
135
136 connector0.Accept(&message);
137 }
138
139 MessageAccumulator accumulator;
140 connector1.set_incoming_receiver(&accumulator);
141
142 PumpMessages();
143
144 for (size_t i = 0; i < MOJO_ARRAYSIZE(kText); ++i) {
145 ASSERT_FALSE(accumulator.IsEmpty());
146
147 Message message_received;
148 accumulator.Pop(&message_received);
149
150 EXPECT_EQ(
151 std::string(kText[i]),
152 std::string(reinterpret_cast<const char*>(message_received.payload())));
153 }
154 }
155
TEST_F(ConnectorTest,WriteToClosedPipe)156 TEST_F(ConnectorTest, WriteToClosedPipe) {
157 internal::Connector connector0(handle0_.Pass());
158
159 const char kText[] = "hello world";
160
161 Message message;
162 AllocMessage(kText, &message);
163
164 // Close the other end of the pipe.
165 handle1_.reset();
166
167 // Not observed yet because we haven't spun the RunLoop yet.
168 EXPECT_FALSE(connector0.encountered_error());
169
170 // Write failures are not reported.
171 bool ok = connector0.Accept(&message);
172 EXPECT_TRUE(ok);
173
174 // Still not observed.
175 EXPECT_FALSE(connector0.encountered_error());
176
177 // Spin the RunLoop, and then we should start observing the closed pipe.
178 PumpMessages();
179
180 EXPECT_TRUE(connector0.encountered_error());
181 }
182
TEST_F(ConnectorTest,MessageWithHandles)183 TEST_F(ConnectorTest, MessageWithHandles) {
184 internal::Connector connector0(handle0_.Pass());
185 internal::Connector connector1(handle1_.Pass());
186
187 const char kText[] = "hello world";
188
189 Message message1;
190 AllocMessage(kText, &message1);
191
192 MessagePipe pipe;
193 message1.mutable_handles()->push_back(pipe.handle0.release());
194
195 connector0.Accept(&message1);
196
197 // The message should have been transferred, releasing the handles.
198 EXPECT_TRUE(message1.handles()->empty());
199
200 MessageAccumulator accumulator;
201 connector1.set_incoming_receiver(&accumulator);
202
203 PumpMessages();
204
205 ASSERT_FALSE(accumulator.IsEmpty());
206
207 Message message_received;
208 accumulator.Pop(&message_received);
209
210 EXPECT_EQ(
211 std::string(kText),
212 std::string(reinterpret_cast<const char*>(message_received.payload())));
213 ASSERT_EQ(1U, message_received.handles()->size());
214
215 // Now send a message to the transferred handle and confirm it's sent through
216 // to the orginal pipe.
217 // TODO(vtl): Do we need a better way of "downcasting" the handle types?
218 ScopedMessagePipeHandle smph;
219 smph.reset(MessagePipeHandle(message_received.handles()->front().value()));
220 message_received.mutable_handles()->front() = Handle();
221 // |smph| now owns this handle.
222
223 internal::Connector connector_received(smph.Pass());
224 internal::Connector connector_original(pipe.handle1.Pass());
225
226 Message message2;
227 AllocMessage(kText, &message2);
228
229 connector_received.Accept(&message2);
230 connector_original.set_incoming_receiver(&accumulator);
231 PumpMessages();
232
233 ASSERT_FALSE(accumulator.IsEmpty());
234
235 accumulator.Pop(&message_received);
236
237 EXPECT_EQ(
238 std::string(kText),
239 std::string(reinterpret_cast<const char*>(message_received.payload())));
240 }
241
242 } // namespace
243 } // namespace test
244 } // namespace mojo
245