1 // Copyright 2014 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 "mojo/system/channel.h"
6
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/message_loop/message_loop.h"
10 #include "mojo/embedder/platform_channel_pair.h"
11 #include "mojo/system/local_message_pipe_endpoint.h"
12 #include "mojo/system/message_in_transit.h"
13 #include "mojo/system/message_pipe.h"
14 #include "mojo/system/proxy_message_pipe_endpoint.h"
15 #include "mojo/system/raw_channel.h"
16 #include "mojo/system/test_utils.h"
17 #include "mojo/system/waiter.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace mojo {
21 namespace system {
22 namespace {
23
24 enum Tristate {
25 TRISTATE_UNKNOWN = -1,
26 TRISTATE_FALSE = 0,
27 TRISTATE_TRUE = 1
28 };
29
BoolToTristate(bool b)30 Tristate BoolToTristate(bool b) {
31 return b ? TRISTATE_TRUE : TRISTATE_FALSE;
32 }
33
34 class ChannelTest : public testing::Test {
35 public:
ChannelTest()36 ChannelTest()
37 : io_thread_(test::TestIOThread::kAutoStart),
38 init_result_(TRISTATE_UNKNOWN) {}
~ChannelTest()39 virtual ~ChannelTest() {}
40
SetUp()41 virtual void SetUp() OVERRIDE {
42 io_thread_.PostTaskAndWait(
43 FROM_HERE,
44 base::Bind(&ChannelTest::SetUpOnIOThread, base::Unretained(this)));
45 }
46
CreateChannelOnIOThread()47 void CreateChannelOnIOThread() {
48 CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
49 channel_ = new Channel();
50 }
51
InitChannelOnIOThread()52 void InitChannelOnIOThread() {
53 CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
54
55 CHECK(raw_channel_);
56 CHECK(channel_);
57 CHECK_EQ(init_result_, TRISTATE_UNKNOWN);
58
59 init_result_ = BoolToTristate(channel_->Init(raw_channel_.Pass()));
60 }
61
ShutdownChannelOnIOThread()62 void ShutdownChannelOnIOThread() {
63 CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
64
65 CHECK(channel_);
66 channel_->Shutdown();
67 }
68
io_thread()69 test::TestIOThread* io_thread() { return &io_thread_; }
raw_channel()70 RawChannel* raw_channel() { return raw_channel_.get(); }
mutable_raw_channel()71 scoped_ptr<RawChannel>* mutable_raw_channel() { return &raw_channel_; }
channel()72 Channel* channel() { return channel_.get(); }
mutable_channel()73 scoped_refptr<Channel>* mutable_channel() { return &channel_; }
init_result() const74 Tristate init_result() const { return init_result_; }
75
76 private:
SetUpOnIOThread()77 void SetUpOnIOThread() {
78 CHECK_EQ(base::MessageLoop::current(), io_thread()->message_loop());
79
80 embedder::PlatformChannelPair channel_pair;
81 raw_channel_ = RawChannel::Create(channel_pair.PassServerHandle()).Pass();
82 other_platform_handle_ = channel_pair.PassClientHandle();
83 }
84
85 test::TestIOThread io_thread_;
86 scoped_ptr<RawChannel> raw_channel_;
87 embedder::ScopedPlatformHandle other_platform_handle_;
88 scoped_refptr<Channel> channel_;
89
90 Tristate init_result_;
91
92 DISALLOW_COPY_AND_ASSIGN(ChannelTest);
93 };
94
95 // ChannelTest.InitShutdown ----------------------------------------------------
96
TEST_F(ChannelTest,InitShutdown)97 TEST_F(ChannelTest, InitShutdown) {
98 io_thread()->PostTaskAndWait(
99 FROM_HERE,
100 base::Bind(&ChannelTest::CreateChannelOnIOThread,
101 base::Unretained(this)));
102 ASSERT_TRUE(channel());
103
104 io_thread()->PostTaskAndWait(
105 FROM_HERE,
106 base::Bind(&ChannelTest::InitChannelOnIOThread,
107 base::Unretained(this)));
108 EXPECT_EQ(TRISTATE_TRUE, init_result());
109
110 io_thread()->PostTaskAndWait(
111 FROM_HERE,
112 base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
113 base::Unretained(this)));
114
115 // Okay to destroy |Channel| on not-the-I/O-thread.
116 EXPECT_TRUE(channel()->HasOneRef());
117 *mutable_channel() = NULL;
118 }
119
120 // ChannelTest.InitFails -------------------------------------------------------
121
122 class MockRawChannelOnInitFails : public RawChannel {
123 public:
MockRawChannelOnInitFails()124 MockRawChannelOnInitFails() : on_init_called_(false) {}
~MockRawChannelOnInitFails()125 virtual ~MockRawChannelOnInitFails() {}
126
127 // |RawChannel| public methods:
GetSerializedPlatformHandleSize() const128 virtual size_t GetSerializedPlatformHandleSize() const OVERRIDE {
129 return 0;
130 }
131
132 private:
133 // |RawChannel| protected methods:
Read(size_t *)134 virtual IOResult Read(size_t*) OVERRIDE {
135 CHECK(false);
136 return IO_FAILED;
137 }
ScheduleRead()138 virtual IOResult ScheduleRead() OVERRIDE {
139 CHECK(false);
140 return IO_FAILED;
141 }
GetReadPlatformHandles(size_t,const void *)142 virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles(
143 size_t, const void*) OVERRIDE {
144 CHECK(false);
145 return embedder::ScopedPlatformHandleVectorPtr();
146 }
WriteNoLock(size_t *,size_t *)147 virtual IOResult WriteNoLock(size_t*, size_t*) OVERRIDE {
148 CHECK(false);
149 return IO_FAILED;
150 }
ScheduleWriteNoLock()151 virtual IOResult ScheduleWriteNoLock() OVERRIDE {
152 CHECK(false);
153 return IO_FAILED;
154 }
OnInit()155 virtual bool OnInit() OVERRIDE {
156 EXPECT_FALSE(on_init_called_);
157 on_init_called_ = true;
158 return false;
159 }
OnShutdownNoLock(scoped_ptr<ReadBuffer>,scoped_ptr<WriteBuffer>)160 virtual void OnShutdownNoLock(scoped_ptr<ReadBuffer>,
161 scoped_ptr<WriteBuffer>) OVERRIDE {
162 CHECK(false);
163 }
164
165 bool on_init_called_;
166
167 DISALLOW_COPY_AND_ASSIGN(MockRawChannelOnInitFails);
168 };
169
TEST_F(ChannelTest,InitFails)170 TEST_F(ChannelTest, InitFails) {
171 io_thread()->PostTaskAndWait(
172 FROM_HERE,
173 base::Bind(&ChannelTest::CreateChannelOnIOThread,
174 base::Unretained(this)));
175 ASSERT_TRUE(channel());
176
177 ASSERT_TRUE(raw_channel());
178 mutable_raw_channel()->reset(new MockRawChannelOnInitFails());
179
180 io_thread()->PostTaskAndWait(
181 FROM_HERE,
182 base::Bind(&ChannelTest::InitChannelOnIOThread,
183 base::Unretained(this)));
184 EXPECT_EQ(TRISTATE_FALSE, init_result());
185
186 // Should destroy |Channel| with no |Shutdown()| (on not-the-I/O-thread).
187 EXPECT_TRUE(channel()->HasOneRef());
188 *mutable_channel() = NULL;
189 }
190
191 // ChannelTest.CloseBeforeRun --------------------------------------------------
192
TEST_F(ChannelTest,CloseBeforeRun)193 TEST_F(ChannelTest, CloseBeforeRun) {
194 io_thread()->PostTaskAndWait(
195 FROM_HERE,
196 base::Bind(&ChannelTest::CreateChannelOnIOThread,
197 base::Unretained(this)));
198 ASSERT_TRUE(channel());
199
200 io_thread()->PostTaskAndWait(
201 FROM_HERE,
202 base::Bind(&ChannelTest::InitChannelOnIOThread,
203 base::Unretained(this)));
204 EXPECT_EQ(TRISTATE_TRUE, init_result());
205
206 scoped_refptr<MessagePipe> mp(new MessagePipe(
207 scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
208 scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
209
210 MessageInTransit::EndpointId local_id =
211 channel()->AttachMessagePipeEndpoint(mp, 1);
212 EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
213
214 mp->Close(0);
215
216 // TODO(vtl): Currently, the |Close()| above won't detach (since it thinks
217 // we're still expecting a "run" message from the other side), so the
218 // |RunMessagePipeEndpoint()| below will return true. We need to refactor
219 // |AttachMessagePipeEndpoint()| to indicate whether |Run...()| will
220 // necessarily be called or not. (Then, in the case that it may not be called,
221 // this will return false.)
222 EXPECT_TRUE(channel()->RunMessagePipeEndpoint(local_id,
223 Channel::kBootstrapEndpointId));
224
225 io_thread()->PostTaskAndWait(
226 FROM_HERE,
227 base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
228 base::Unretained(this)));
229
230 EXPECT_TRUE(channel()->HasOneRef());
231 }
232
233 // ChannelTest.ShutdownAfterAttachAndRun ---------------------------------------
234
TEST_F(ChannelTest,ShutdownAfterAttach)235 TEST_F(ChannelTest, ShutdownAfterAttach) {
236 io_thread()->PostTaskAndWait(
237 FROM_HERE,
238 base::Bind(&ChannelTest::CreateChannelOnIOThread,
239 base::Unretained(this)));
240 ASSERT_TRUE(channel());
241
242 io_thread()->PostTaskAndWait(
243 FROM_HERE,
244 base::Bind(&ChannelTest::InitChannelOnIOThread,
245 base::Unretained(this)));
246 EXPECT_EQ(TRISTATE_TRUE, init_result());
247
248 scoped_refptr<MessagePipe> mp(new MessagePipe(
249 scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
250 scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
251
252 MessageInTransit::EndpointId local_id =
253 channel()->AttachMessagePipeEndpoint(mp, 1);
254 EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
255
256 // TODO(vtl): Currently, we always "expect" a |RunMessagePipeEndpoint()| after
257 // an |AttachMessagePipeEndpoint()| (which is actually incorrect). We need to
258 // refactor |AttachMessagePipeEndpoint()| to indicate whether |Run...()| will
259 // necessarily be called or not. (Then, in the case that it may not be called,
260 // we should test a |Shutdown()| without the |Run...()|.)
261 EXPECT_TRUE(channel()->RunMessagePipeEndpoint(local_id,
262 Channel::kBootstrapEndpointId));
263
264 Waiter waiter;
265 waiter.Init();
266 EXPECT_EQ(MOJO_RESULT_OK,
267 mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
268
269 // Don't wait for the shutdown to run ...
270 io_thread()->PostTask(FROM_HERE,
271 base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
272 base::Unretained(this)));
273
274 // ... since this |Wait()| should fail once the channel is shut down.
275 EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
276 waiter.Wait(MOJO_DEADLINE_INDEFINITE, NULL));
277 mp->RemoveWaiter(0, &waiter);
278
279 mp->Close(0);
280
281 EXPECT_TRUE(channel()->HasOneRef());
282 }
283
284 // ChannelTest.WaitAfterAttachRunAndShutdown -----------------------------------
285
TEST_F(ChannelTest,WaitAfterAttachRunAndShutdown)286 TEST_F(ChannelTest, WaitAfterAttachRunAndShutdown) {
287 io_thread()->PostTaskAndWait(
288 FROM_HERE,
289 base::Bind(&ChannelTest::CreateChannelOnIOThread,
290 base::Unretained(this)));
291 ASSERT_TRUE(channel());
292
293 io_thread()->PostTaskAndWait(
294 FROM_HERE,
295 base::Bind(&ChannelTest::InitChannelOnIOThread,
296 base::Unretained(this)));
297 EXPECT_EQ(TRISTATE_TRUE, init_result());
298
299 scoped_refptr<MessagePipe> mp(new MessagePipe(
300 scoped_ptr<MessagePipeEndpoint>(new LocalMessagePipeEndpoint()),
301 scoped_ptr<MessagePipeEndpoint>(new ProxyMessagePipeEndpoint())));
302
303 MessageInTransit::EndpointId local_id =
304 channel()->AttachMessagePipeEndpoint(mp, 1);
305 EXPECT_EQ(Channel::kBootstrapEndpointId, local_id);
306
307 EXPECT_TRUE(channel()->RunMessagePipeEndpoint(local_id,
308 Channel::kBootstrapEndpointId));
309
310 io_thread()->PostTaskAndWait(
311 FROM_HERE,
312 base::Bind(&ChannelTest::ShutdownChannelOnIOThread,
313 base::Unretained(this)));
314
315 Waiter waiter;
316 waiter.Init();
317 EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
318 mp->AddWaiter(0, &waiter, MOJO_HANDLE_SIGNAL_READABLE, 123));
319
320 mp->Close(0);
321
322 EXPECT_TRUE(channel()->HasOneRef());
323 }
324
325 // TODO(vtl): More. ------------------------------------------------------------
326
327 } // namespace
328 } // namespace system
329 } // namespace mojo
330