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.
6 #include "base/message_loop/message_loop.h"
7 #include "base/run_loop.h"
8 #include "extensions/browser/mojo/stash_backend.h"
9 #include "mojo/application/public/interfaces/service_provider.mojom.h"
10 #include "testing/gtest/include/gtest/gtest.h"
12 namespace extensions
{
15 // Create a data pipe, write some data to the producer handle and return the
17 mojo::ScopedHandle
CreateReadableHandle() {
18 mojo::ScopedDataPipeConsumerHandle consumer_handle
;
19 mojo::ScopedDataPipeProducerHandle producer_handle
;
20 MojoCreateDataPipeOptions options
= {
21 sizeof(options
), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
, 1, 1,
24 mojo::CreateDataPipe(&options
, &producer_handle
, &consumer_handle
);
25 EXPECT_EQ(MOJO_RESULT_OK
, result
);
26 uint32_t num_bytes
= 1;
27 result
= mojo::WriteDataRaw(producer_handle
.get(), "a", &num_bytes
,
28 MOJO_WRITE_DATA_FLAG_NONE
);
29 EXPECT_EQ(MOJO_RESULT_OK
, result
);
30 EXPECT_EQ(1u, num_bytes
);
31 return mojo::ScopedHandle::From(consumer_handle
.Pass());
36 class StashServiceTest
: public testing::Test
{
40 EVENT_STASH_RETRIEVED
,
46 void SetUp() override
{
47 expecting_error_
= false;
48 expected_event_
= EVENT_NONE
;
49 stash_backend_
.reset(new StashBackend(base::Bind(
50 &StashServiceTest::OnHandleReadyToRead
, base::Unretained(this))));
51 stash_backend_
->BindToRequest(mojo::GetProxy(&stash_service_
));
52 stash_service_
.set_connection_error_handler(base::Bind(&OnConnectionError
));
56 static void OnConnectionError() { FAIL() << "Unexpected connection error"; }
58 mojo::Array
<StashedObjectPtr
> RetrieveStash() {
59 mojo::Array
<StashedObjectPtr
> stash
;
60 stash_service_
->RetrieveStash(base::Bind(
61 &StashServiceTest::StashRetrieved
, base::Unretained(this), &stash
));
62 WaitForEvent(EVENT_STASH_RETRIEVED
);
66 void StashRetrieved(mojo::Array
<StashedObjectPtr
>* output
,
67 mojo::Array
<StashedObjectPtr
> stash
) {
68 *output
= stash
.Pass();
69 EventReceived(EVENT_STASH_RETRIEVED
);
72 void WaitForEvent(Event event
) {
73 expected_event_
= event
;
74 base::RunLoop run_loop
;
75 stop_run_loop_
= run_loop
.QuitClosure();
79 void EventReceived(Event event
) {
80 if (event
== expected_event_
&& !stop_run_loop_
.is_null())
84 void OnHandleReadyToRead() {
86 EventReceived(EVENT_HANDLE_READY
);
90 base::MessageLoop message_loop_
;
91 base::Closure stop_run_loop_
;
92 scoped_ptr
<StashBackend
> stash_backend_
;
93 Event expected_event_
;
94 bool expecting_error_
;
95 mojo::InterfacePtr
<StashService
> stash_service_
;
99 DISALLOW_COPY_AND_ASSIGN(StashServiceTest
);
102 // Test that adding stashed objects in multiple calls can all be retrieved by a
104 TEST_F(StashServiceTest
, AddTwiceAndRetrieve
) {
105 mojo::Array
<StashedObjectPtr
> stashed_objects
;
106 StashedObjectPtr
stashed_object(StashedObject::New());
107 stashed_object
->id
= "test type";
108 stashed_object
->data
.push_back(1);
109 stashed_object
->stashed_handles
= mojo::Array
<mojo::ScopedHandle
>(0);
110 stashed_objects
.push_back(stashed_object
.Pass());
111 stash_service_
->AddToStash(stashed_objects
.Pass());
112 stashed_object
= StashedObject::New();
113 stashed_object
->id
= "test type2";
114 stashed_object
->data
.push_back(2);
115 stashed_object
->data
.push_back(3);
116 stashed_object
->stashed_handles
= mojo::Array
<mojo::ScopedHandle
>(0);
117 stashed_objects
.push_back(stashed_object
.Pass());
118 stash_service_
->AddToStash(stashed_objects
.Pass());
119 stashed_objects
= RetrieveStash();
120 ASSERT_EQ(2u, stashed_objects
.size());
121 EXPECT_EQ("test type", stashed_objects
[0]->id
);
122 EXPECT_EQ(0u, stashed_objects
[0]->stashed_handles
.size());
123 EXPECT_EQ(1u, stashed_objects
[0]->data
.size());
124 EXPECT_EQ(1, stashed_objects
[0]->data
[0]);
125 EXPECT_EQ("test type2", stashed_objects
[1]->id
);
126 EXPECT_EQ(0u, stashed_objects
[1]->stashed_handles
.size());
127 EXPECT_EQ(2u, stashed_objects
[1]->data
.size());
128 EXPECT_EQ(2, stashed_objects
[1]->data
[0]);
129 EXPECT_EQ(3, stashed_objects
[1]->data
[1]);
132 // Test that handles survive a round-trip through the stash.
133 TEST_F(StashServiceTest
, StashAndRetrieveHandles
) {
134 mojo::Array
<StashedObjectPtr
> stashed_objects
;
135 StashedObjectPtr
stashed_object(StashedObject::New());
136 stashed_object
->id
= "test type";
137 stashed_object
->data
.push_back(1);
139 mojo::ScopedDataPipeConsumerHandle consumer
;
140 mojo::ScopedDataPipeProducerHandle producer
;
141 MojoCreateDataPipeOptions options
= {
142 sizeof(options
), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
, 1, 1,
144 mojo::CreateDataPipe(&options
, &producer
, &consumer
);
145 uint32_t num_bytes
= 1;
146 MojoResult result
= mojo::WriteDataRaw(
147 producer
.get(), "1", &num_bytes
, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE
);
148 ASSERT_EQ(MOJO_RESULT_OK
, result
);
149 ASSERT_EQ(1u, num_bytes
);
151 stashed_object
->stashed_handles
.push_back(
152 mojo::ScopedHandle::From(producer
.Pass()));
153 stashed_object
->stashed_handles
.push_back(
154 mojo::ScopedHandle::From(consumer
.Pass()));
155 stashed_objects
.push_back(stashed_object
.Pass());
156 stash_service_
->AddToStash(stashed_objects
.Pass());
157 stashed_objects
= RetrieveStash();
158 ASSERT_EQ(1u, stashed_objects
.size());
159 EXPECT_EQ("test type", stashed_objects
[0]->id
);
160 ASSERT_EQ(2u, stashed_objects
[0]->stashed_handles
.size());
162 consumer
= mojo::ScopedDataPipeConsumerHandle::From(
163 stashed_objects
[0]->stashed_handles
[1].Pass());
165 consumer
.get(), MOJO_HANDLE_SIGNAL_READABLE
, MOJO_DEADLINE_INDEFINITE
,
167 ASSERT_EQ(MOJO_RESULT_OK
, result
);
169 result
= mojo::ReadDataRaw(
170 consumer
.get(), &data
, &num_bytes
, MOJO_READ_DATA_FLAG_ALL_OR_NONE
);
171 ASSERT_EQ(MOJO_RESULT_OK
, result
);
172 ASSERT_EQ(1u, num_bytes
);
173 EXPECT_EQ('1', data
);
176 TEST_F(StashServiceTest
, RetrieveWithoutStashing
) {
177 mojo::Array
<StashedObjectPtr
> stashed_objects
= RetrieveStash();
178 ASSERT_TRUE(!stashed_objects
.is_null());
179 EXPECT_EQ(0u, stashed_objects
.size());
182 TEST_F(StashServiceTest
, NotifyOnReadableHandle
) {
183 mojo::Array
<StashedObjectPtr
> stash_entries
;
184 StashedObjectPtr
stashed_object(StashedObject::New());
185 stashed_object
->id
= "test type";
186 stashed_object
->data
.push_back(0);
187 stashed_object
->monitor_handles
= true;
188 mojo::ServiceProviderPtr service_provider
;
190 // Stash the ServiceProvider request. When we make a call on
191 // |service_provider|, the stashed handle will become readable.
192 stashed_object
->stashed_handles
.push_back(mojo::ScopedHandle::From(
193 mojo::GetProxy(&service_provider
).PassMessagePipe()));
195 stash_entries
.push_back(stashed_object
.Pass());
196 stash_service_
->AddToStash(stash_entries
.Pass());
198 mojo::MessagePipe pipe
;
199 service_provider
->ConnectToService("", pipe
.handle0
.Pass());
201 WaitForEvent(EVENT_HANDLE_READY
);
202 EXPECT_EQ(1, handles_ready_
);
205 TEST_F(StashServiceTest
, NotifyOnReadableDataPipeHandle
) {
206 mojo::Array
<StashedObjectPtr
> stash_entries
;
207 StashedObjectPtr
stashed_object(StashedObject::New());
208 stashed_object
->id
= "test type";
209 stashed_object
->monitor_handles
= true;
211 MojoCreateDataPipeOptions options
= {
212 sizeof(options
), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE
, 1, 1,
214 mojo::ScopedDataPipeConsumerHandle consumer_handle
;
215 mojo::ScopedDataPipeProducerHandle producer_handle
;
216 uint32_t num_bytes
= 1;
218 mojo::CreateDataPipe(&options
, &producer_handle
, &consumer_handle
);
219 ASSERT_EQ(MOJO_RESULT_OK
, result
);
220 result
= mojo::WriteDataRaw(producer_handle
.get(), "a", &num_bytes
,
221 MOJO_WRITE_DATA_FLAG_NONE
);
222 ASSERT_EQ(MOJO_RESULT_OK
, result
);
223 ASSERT_EQ(1u, num_bytes
);
224 stashed_object
->stashed_handles
.push_back(
225 mojo::ScopedHandle::From(producer_handle
.Pass()));
226 stashed_object
->stashed_handles
.push_back(
227 mojo::ScopedHandle::From(consumer_handle
.Pass()));
228 stashed_object
->data
.push_back(1);
230 stash_entries
.push_back(stashed_object
.Pass());
231 stash_service_
->AddToStash(stash_entries
.Pass());
232 WaitForEvent(EVENT_HANDLE_READY
);
233 EXPECT_EQ(1, handles_ready_
);
236 TEST_F(StashServiceTest
, NotifyOncePerStashOnReadableHandles
) {
237 mojo::Array
<StashedObjectPtr
> stash_entries
;
238 StashedObjectPtr
stashed_object(StashedObject::New());
239 stashed_object
->id
= "test type";
240 stashed_object
->data
.push_back(1);
241 stashed_object
->monitor_handles
= true;
242 stashed_object
->stashed_handles
.push_back(CreateReadableHandle());
243 stashed_object
->stashed_handles
.push_back(CreateReadableHandle());
244 stash_entries
.push_back(stashed_object
.Pass());
245 stashed_object
= StashedObject::New();
246 stashed_object
->id
= "another test type";
247 stashed_object
->data
.push_back(2);
248 stashed_object
->monitor_handles
= true;
249 stashed_object
->stashed_handles
.push_back(CreateReadableHandle());
250 stashed_object
->stashed_handles
.push_back(CreateReadableHandle());
251 stash_entries
.push_back(stashed_object
.Pass());
252 stash_service_
->AddToStash(stash_entries
.Pass());
253 WaitForEvent(EVENT_HANDLE_READY
);
254 EXPECT_EQ(1, handles_ready_
);
256 stashed_object
= StashedObject::New();
257 stashed_object
->id
= "yet another test type";
258 stashed_object
->data
.push_back(3);
259 stashed_object
->monitor_handles
= true;
260 stashed_object
->stashed_handles
.push_back(CreateReadableHandle());
261 stashed_object
->stashed_handles
.push_back(CreateReadableHandle());
262 stash_entries
.push_back(stashed_object
.Pass());
263 stash_service_
->AddToStash(stash_entries
.Pass());
265 stash_service_
->AddToStash(RetrieveStash());
266 WaitForEvent(EVENT_HANDLE_READY
);
267 EXPECT_EQ(2, handles_ready_
);
270 // Test that a stash service discards stashed objects when the backend no longer
272 TEST_F(StashServiceTest
, ServiceWithDeletedBackend
) {
273 stash_backend_
.reset();
274 stash_service_
.set_connection_error_handler(base::Bind(&OnConnectionError
));
276 mojo::Array
<StashedObjectPtr
> stashed_objects
;
277 StashedObjectPtr
stashed_object(StashedObject::New());
278 stashed_object
->id
= "test type";
279 stashed_object
->data
.push_back(1);
280 mojo::MessagePipe message_pipe
;
281 stashed_object
->stashed_handles
.push_back(
282 mojo::ScopedHandle::From(message_pipe
.handle0
.Pass()));
283 stashed_objects
.push_back(stashed_object
.Pass());
284 stash_service_
->AddToStash(stashed_objects
.Pass());
285 stashed_objects
= RetrieveStash();
286 ASSERT_EQ(0u, stashed_objects
.size());
287 // Check that the stashed handle has been closed.
289 mojo::Wait(message_pipe
.handle1
.get(),
290 MOJO_HANDLE_SIGNAL_READABLE
| MOJO_HANDLE_SIGNAL_READABLE
,
291 MOJO_DEADLINE_INDEFINITE
, nullptr);
292 EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION
, result
);
295 } // namespace extensions