blob: cf69f98c43be9f853e4192862f40c05b651a3543 [file] [log] [blame]
sammca0a73e52014-10-07 04:53:161// 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
dchenge59eca1602015-12-18 17:48:005#include "extensions/browser/mojo/stash_backend.h"
6
avic9cec102015-12-23 00:39:267#include <stdint.h>
8
dchenge59eca1602015-12-18 17:48:009#include <utility>
10
sammca0a73e52014-10-07 04:53:1611#include "base/bind.h"
avic9cec102015-12-23 00:39:2612#include "base/macros.h"
sammca0a73e52014-10-07 04:53:1613#include "base/message_loop/message_loop.h"
14#include "base/run_loop.h"
ben95787952016-02-10 07:46:2015#include "mojo/shell/public/interfaces/interface_provider.mojom.h"
sammca0a73e52014-10-07 04:53:1616#include "testing/gtest/include/gtest/gtest.h"
17
18namespace extensions {
sammccf7d0822015-02-06 06:46:2819namespace {
20
21// Create a data pipe, write some data to the producer handle and return the
22// consumer handle.
23mojo::ScopedHandle CreateReadableHandle() {
24 mojo::ScopedDataPipeConsumerHandle consumer_handle;
25 mojo::ScopedDataPipeProducerHandle producer_handle;
26 MojoCreateDataPipeOptions options = {
27 sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, 1,
28 };
29 MojoResult result =
30 mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle);
31 EXPECT_EQ(MOJO_RESULT_OK, result);
32 uint32_t num_bytes = 1;
33 result = mojo::WriteDataRaw(producer_handle.get(), "a", &num_bytes,
34 MOJO_WRITE_DATA_FLAG_NONE);
35 EXPECT_EQ(MOJO_RESULT_OK, result);
36 EXPECT_EQ(1u, num_bytes);
dchenge59eca1602015-12-18 17:48:0037 return mojo::ScopedHandle::From(std::move(consumer_handle));
sammccf7d0822015-02-06 06:46:2838}
39
40} // namespace
sammca0a73e52014-10-07 04:53:1641
rockotb6a14b72015-08-24 17:24:1942class StashServiceTest : public testing::Test {
sammca0a73e52014-10-07 04:53:1643 public:
44 enum Event {
45 EVENT_NONE,
46 EVENT_STASH_RETRIEVED,
sammccf7d0822015-02-06 06:46:2847 EVENT_HANDLE_READY,
sammca0a73e52014-10-07 04:53:1648 };
49
50 StashServiceTest() {}
51
dchengf9afb372014-10-27 21:43:1452 void SetUp() override {
sammca0a73e52014-10-07 04:53:1653 expecting_error_ = false;
54 expected_event_ = EVENT_NONE;
sammccf7d0822015-02-06 06:46:2855 stash_backend_.reset(new StashBackend(base::Bind(
56 &StashServiceTest::OnHandleReadyToRead, base::Unretained(this))));
sammca0a73e52014-10-07 04:53:1657 stash_backend_->BindToRequest(mojo::GetProxy(&stash_service_));
rockotb6a14b72015-08-24 17:24:1958 stash_service_.set_connection_error_handler(base::Bind(&OnConnectionError));
sammccf7d0822015-02-06 06:46:2859 handles_ready_ = 0;
sammca0a73e52014-10-07 04:53:1660 }
61
rockotb6a14b72015-08-24 17:24:1962 static void OnConnectionError() { FAIL() << "Unexpected connection error"; }
sammca0a73e52014-10-07 04:53:1663
64 mojo::Array<StashedObjectPtr> RetrieveStash() {
65 mojo::Array<StashedObjectPtr> stash;
66 stash_service_->RetrieveStash(base::Bind(
67 &StashServiceTest::StashRetrieved, base::Unretained(this), &stash));
68 WaitForEvent(EVENT_STASH_RETRIEVED);
dchenge59eca1602015-12-18 17:48:0069 return stash;
sammca0a73e52014-10-07 04:53:1670 }
71
72 void StashRetrieved(mojo::Array<StashedObjectPtr>* output,
73 mojo::Array<StashedObjectPtr> stash) {
dchenge59eca1602015-12-18 17:48:0074 *output = std::move(stash);
sammca0a73e52014-10-07 04:53:1675 EventReceived(EVENT_STASH_RETRIEVED);
76 }
77
78 void WaitForEvent(Event event) {
79 expected_event_ = event;
80 base::RunLoop run_loop;
81 stop_run_loop_ = run_loop.QuitClosure();
82 run_loop.Run();
83 }
84
85 void EventReceived(Event event) {
86 if (event == expected_event_ && !stop_run_loop_.is_null())
87 stop_run_loop_.Run();
88 }
89
sammccf7d0822015-02-06 06:46:2890 void OnHandleReadyToRead() {
91 handles_ready_++;
92 EventReceived(EVENT_HANDLE_READY);
93 }
94
sammca0a73e52014-10-07 04:53:1695 protected:
96 base::MessageLoop message_loop_;
97 base::Closure stop_run_loop_;
98 scoped_ptr<StashBackend> stash_backend_;
99 Event expected_event_;
100 bool expecting_error_;
101 mojo::InterfacePtr<StashService> stash_service_;
sammccf7d0822015-02-06 06:46:28102 int handles_ready_;
sammca0a73e52014-10-07 04:53:16103
104 private:
105 DISALLOW_COPY_AND_ASSIGN(StashServiceTest);
106};
107
108// Test that adding stashed objects in multiple calls can all be retrieved by a
109// Retrieve call.
110TEST_F(StashServiceTest, AddTwiceAndRetrieve) {
111 mojo::Array<StashedObjectPtr> stashed_objects;
112 StashedObjectPtr stashed_object(StashedObject::New());
113 stashed_object->id = "test type";
114 stashed_object->data.push_back(1);
yzshen48f0c962016-02-13 01:10:46115 stashed_object->stashed_handles = mojo::Array<mojo::ScopedHandle>();
dchenge59eca1602015-12-18 17:48:00116 stashed_objects.push_back(std::move(stashed_object));
117 stash_service_->AddToStash(std::move(stashed_objects));
sammca0a73e52014-10-07 04:53:16118 stashed_object = StashedObject::New();
119 stashed_object->id = "test type2";
120 stashed_object->data.push_back(2);
121 stashed_object->data.push_back(3);
yzshen48f0c962016-02-13 01:10:46122 stashed_object->stashed_handles = mojo::Array<mojo::ScopedHandle>();
dchenge59eca1602015-12-18 17:48:00123 stashed_objects.push_back(std::move(stashed_object));
124 stash_service_->AddToStash(std::move(stashed_objects));
sammca0a73e52014-10-07 04:53:16125 stashed_objects = RetrieveStash();
126 ASSERT_EQ(2u, stashed_objects.size());
127 EXPECT_EQ("test type", stashed_objects[0]->id);
128 EXPECT_EQ(0u, stashed_objects[0]->stashed_handles.size());
129 EXPECT_EQ(1u, stashed_objects[0]->data.size());
130 EXPECT_EQ(1, stashed_objects[0]->data[0]);
131 EXPECT_EQ("test type2", stashed_objects[1]->id);
132 EXPECT_EQ(0u, stashed_objects[1]->stashed_handles.size());
133 EXPECT_EQ(2u, stashed_objects[1]->data.size());
134 EXPECT_EQ(2, stashed_objects[1]->data[0]);
135 EXPECT_EQ(3, stashed_objects[1]->data[1]);
136}
137
138// Test that handles survive a round-trip through the stash.
139TEST_F(StashServiceTest, StashAndRetrieveHandles) {
140 mojo::Array<StashedObjectPtr> stashed_objects;
141 StashedObjectPtr stashed_object(StashedObject::New());
142 stashed_object->id = "test type";
143 stashed_object->data.push_back(1);
144
145 mojo::ScopedDataPipeConsumerHandle consumer;
146 mojo::ScopedDataPipeProducerHandle producer;
147 MojoCreateDataPipeOptions options = {
148 sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, 1,
149 };
150 mojo::CreateDataPipe(&options, &producer, &consumer);
151 uint32_t num_bytes = 1;
152 MojoResult result = mojo::WriteDataRaw(
153 producer.get(), "1", &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
154 ASSERT_EQ(MOJO_RESULT_OK, result);
155 ASSERT_EQ(1u, num_bytes);
156
157 stashed_object->stashed_handles.push_back(
dchenge59eca1602015-12-18 17:48:00158 mojo::ScopedHandle::From(std::move(producer)));
sammca0a73e52014-10-07 04:53:16159 stashed_object->stashed_handles.push_back(
dchenge59eca1602015-12-18 17:48:00160 mojo::ScopedHandle::From(std::move(consumer)));
161 stashed_objects.push_back(std::move(stashed_object));
162 stash_service_->AddToStash(std::move(stashed_objects));
sammca0a73e52014-10-07 04:53:16163 stashed_objects = RetrieveStash();
164 ASSERT_EQ(1u, stashed_objects.size());
165 EXPECT_EQ("test type", stashed_objects[0]->id);
166 ASSERT_EQ(2u, stashed_objects[0]->stashed_handles.size());
167
168 consumer = mojo::ScopedDataPipeConsumerHandle::From(
dchenge59eca1602015-12-18 17:48:00169 std::move(stashed_objects[0]->stashed_handles[1]));
sammca0a73e52014-10-07 04:53:16170 result = mojo::Wait(
rockot6dcbf7c2015-01-16 00:41:13171 consumer.get(), MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE,
172 nullptr);
sammca0a73e52014-10-07 04:53:16173 ASSERT_EQ(MOJO_RESULT_OK, result);
174 char data = '\0';
175 result = mojo::ReadDataRaw(
176 consumer.get(), &data, &num_bytes, MOJO_READ_DATA_FLAG_ALL_OR_NONE);
177 ASSERT_EQ(MOJO_RESULT_OK, result);
178 ASSERT_EQ(1u, num_bytes);
179 EXPECT_EQ('1', data);
180}
181
182TEST_F(StashServiceTest, RetrieveWithoutStashing) {
183 mojo::Array<StashedObjectPtr> stashed_objects = RetrieveStash();
184 ASSERT_TRUE(!stashed_objects.is_null());
185 EXPECT_EQ(0u, stashed_objects.size());
186}
187
sammccf7d0822015-02-06 06:46:28188TEST_F(StashServiceTest, NotifyOnReadableHandle) {
189 mojo::Array<StashedObjectPtr> stash_entries;
190 StashedObjectPtr stashed_object(StashedObject::New());
191 stashed_object->id = "test type";
192 stashed_object->data.push_back(0);
193 stashed_object->monitor_handles = true;
ben80ab89052016-02-11 20:19:27194 mojo::shell::mojom::InterfaceProviderPtr service_provider;
sammccf7d0822015-02-06 06:46:28195
196 // Stash the ServiceProvider request. When we make a call on
197 // |service_provider|, the stashed handle will become readable.
198 stashed_object->stashed_handles.push_back(mojo::ScopedHandle::From(
199 mojo::GetProxy(&service_provider).PassMessagePipe()));
200
dchenge59eca1602015-12-18 17:48:00201 stash_entries.push_back(std::move(stashed_object));
202 stash_service_->AddToStash(std::move(stash_entries));
sammccf7d0822015-02-06 06:46:28203
204 mojo::MessagePipe pipe;
ben95787952016-02-10 07:46:20205 service_provider->GetInterface("", std::move(pipe.handle0));
sammccf7d0822015-02-06 06:46:28206
207 WaitForEvent(EVENT_HANDLE_READY);
208 EXPECT_EQ(1, handles_ready_);
209}
210
211TEST_F(StashServiceTest, NotifyOnReadableDataPipeHandle) {
212 mojo::Array<StashedObjectPtr> stash_entries;
213 StashedObjectPtr stashed_object(StashedObject::New());
214 stashed_object->id = "test type";
215 stashed_object->monitor_handles = true;
216
217 MojoCreateDataPipeOptions options = {
218 sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, 1,
219 };
220 mojo::ScopedDataPipeConsumerHandle consumer_handle;
221 mojo::ScopedDataPipeProducerHandle producer_handle;
222 uint32_t num_bytes = 1;
223 MojoResult result =
224 mojo::CreateDataPipe(&options, &producer_handle, &consumer_handle);
225 ASSERT_EQ(MOJO_RESULT_OK, result);
226 result = mojo::WriteDataRaw(producer_handle.get(), "a", &num_bytes,
227 MOJO_WRITE_DATA_FLAG_NONE);
228 ASSERT_EQ(MOJO_RESULT_OK, result);
229 ASSERT_EQ(1u, num_bytes);
230 stashed_object->stashed_handles.push_back(
dchenge59eca1602015-12-18 17:48:00231 mojo::ScopedHandle::From(std::move(producer_handle)));
sammccf7d0822015-02-06 06:46:28232 stashed_object->stashed_handles.push_back(
dchenge59eca1602015-12-18 17:48:00233 mojo::ScopedHandle::From(std::move(consumer_handle)));
sammccf7d0822015-02-06 06:46:28234 stashed_object->data.push_back(1);
235
dchenge59eca1602015-12-18 17:48:00236 stash_entries.push_back(std::move(stashed_object));
237 stash_service_->AddToStash(std::move(stash_entries));
sammccf7d0822015-02-06 06:46:28238 WaitForEvent(EVENT_HANDLE_READY);
239 EXPECT_EQ(1, handles_ready_);
240}
241
242TEST_F(StashServiceTest, NotifyOncePerStashOnReadableHandles) {
243 mojo::Array<StashedObjectPtr> stash_entries;
244 StashedObjectPtr stashed_object(StashedObject::New());
245 stashed_object->id = "test type";
246 stashed_object->data.push_back(1);
247 stashed_object->monitor_handles = true;
248 stashed_object->stashed_handles.push_back(CreateReadableHandle());
249 stashed_object->stashed_handles.push_back(CreateReadableHandle());
dchenge59eca1602015-12-18 17:48:00250 stash_entries.push_back(std::move(stashed_object));
sammccf7d0822015-02-06 06:46:28251 stashed_object = StashedObject::New();
252 stashed_object->id = "another test type";
253 stashed_object->data.push_back(2);
254 stashed_object->monitor_handles = true;
255 stashed_object->stashed_handles.push_back(CreateReadableHandle());
256 stashed_object->stashed_handles.push_back(CreateReadableHandle());
dchenge59eca1602015-12-18 17:48:00257 stash_entries.push_back(std::move(stashed_object));
258 stash_service_->AddToStash(std::move(stash_entries));
sammccf7d0822015-02-06 06:46:28259 WaitForEvent(EVENT_HANDLE_READY);
260 EXPECT_EQ(1, handles_ready_);
261
262 stashed_object = StashedObject::New();
263 stashed_object->id = "yet another test type";
264 stashed_object->data.push_back(3);
265 stashed_object->monitor_handles = true;
266 stashed_object->stashed_handles.push_back(CreateReadableHandle());
267 stashed_object->stashed_handles.push_back(CreateReadableHandle());
dchenge59eca1602015-12-18 17:48:00268 stash_entries.push_back(std::move(stashed_object));
269 stash_service_->AddToStash(std::move(stash_entries));
sammccf7d0822015-02-06 06:46:28270
271 stash_service_->AddToStash(RetrieveStash());
272 WaitForEvent(EVENT_HANDLE_READY);
273 EXPECT_EQ(2, handles_ready_);
274}
275
sammca0a73e52014-10-07 04:53:16276// Test that a stash service discards stashed objects when the backend no longer
277// exists.
278TEST_F(StashServiceTest, ServiceWithDeletedBackend) {
279 stash_backend_.reset();
rockotb6a14b72015-08-24 17:24:19280 stash_service_.set_connection_error_handler(base::Bind(&OnConnectionError));
sammca0a73e52014-10-07 04:53:16281
282 mojo::Array<StashedObjectPtr> stashed_objects;
283 StashedObjectPtr stashed_object(StashedObject::New());
284 stashed_object->id = "test type";
285 stashed_object->data.push_back(1);
286 mojo::MessagePipe message_pipe;
287 stashed_object->stashed_handles.push_back(
dchenge59eca1602015-12-18 17:48:00288 mojo::ScopedHandle::From(std::move(message_pipe.handle0)));
289 stashed_objects.push_back(std::move(stashed_object));
290 stash_service_->AddToStash(std::move(stashed_objects));
sammca0a73e52014-10-07 04:53:16291 stashed_objects = RetrieveStash();
292 ASSERT_EQ(0u, stashed_objects.size());
293 // Check that the stashed handle has been closed.
294 MojoResult result =
295 mojo::Wait(message_pipe.handle1.get(),
296 MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_READABLE,
rockot6dcbf7c2015-01-16 00:41:13297 MOJO_DEADLINE_INDEFINITE, nullptr);
sammca0a73e52014-10-07 04:53:16298 EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
299}
300
301} // namespace extensions