Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(640)

Side by Side Diff: mojo/services/files/public/cpp/tests/input_stream_file_unittest.cc

Issue 1388413005: Move //mojo/services/X/public/... to //mojo/services/X/... (part 1). (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "files/public/cpp/input_stream_file.h"
6
7 #include <string.h>
8
9 #include <string>
10 #include <utility>
11
12 #include "files/public/interfaces/files.mojom.h"
13 #include "files/public/interfaces/types.mojom.h"
14 #include "mojo/public/cpp/application/application_test_base.h"
15 #include "mojo/public/cpp/bindings/interface_request.h"
16 #include "mojo/public/cpp/environment/logging.h"
17 #include "mojo/public/cpp/system/macros.h"
18 #include "mojo/public/cpp/utility/run_loop.h"
19
20 namespace files_impl {
21 namespace {
22
23 using InputStreamFileTest = mojo::test::ApplicationTestBase;
24
25 void QuitMessageLoop() {
26 mojo::RunLoop::current()->Quit();
27 }
28
29 void RunMessageLoop() {
30 mojo::RunLoop::current()->Run();
31 }
32
33 void RunMessageLoopUntilIdle() {
34 mojo::RunLoop::current()->RunUntilIdle();
35 }
36
37 void PostTaskToMessageLoop(const mojo::Closure& task) {
38 mojo::RunLoop::current()->PostDelayedTask(task, 0);
39 }
40
41 // Converts a string to a |mojo::Array<uint8_t>|.
42 mojo::Array<uint8_t> StringToArray(const std::string& s) {
43 auto rv = mojo::Array<uint8_t>::New(s.size());
44 if (s.size())
45 memcpy(&rv[0], &s[0], s.size());
46 return rv;
47 }
48
49 // Converts a |mojo::Array<uint8_t>| to a string. If the array is null, returns
50 // the string "ARRAY_IS_NULL".
51 std::string ArrayToString(const mojo::Array<uint8_t>& a) {
52 if (a.is_null())
53 return std::string("ARRAY_IS_NULL");
54 return a.size() ? std::string(reinterpret_cast<const char*>(&a[0]), a.size())
55 : std::string();
56 }
57
58 class TestClient : public InputStreamFile::Client {
59 public:
60 TestClient() { Reset(); }
61 ~TestClient() override {}
62
63 // Note: This doesn't reset |callback_|.
64 void Reset() {
65 data_ = StringToArray("OOPS");
66 complete_synchronously_ = true;
67 got_request_data_ = false;
68 got_on_closed_ = false;
69 }
70
71 // Completes a pending callback. Note: |RequestData()| may be called again
72 // "inside" this (i.e., "inside" the callback).
73 void RunRequestDataCallback(mojo::Array<uint8_t> data) {
74 MOJO_CHECK(!callback_.is_null());
75 RequestDataCallback callback;
76 std::swap(callback, callback_);
77 callback.Run(mojo::files::Error::OK, data.Pass());
78 }
79
80 void set_data(mojo::Array<uint8_t> data) {
81 MOJO_CHECK(!data.is_null());
82 data_ = data.Pass();
83 }
84 void set_complete_synchronously(bool complete_synchronously) {
85 complete_synchronously_ = complete_synchronously;
86 }
87
88 bool got_request_data() const { return got_request_data_; }
89 bool got_on_closed() const { return got_on_closed_; }
90
91 private:
92 // |InputStreamFile::Client|:
93 bool RequestData(size_t max_num_bytes,
94 mojo::files::Error* error,
95 mojo::Array<uint8_t>* data,
96 const RequestDataCallback& callback) override {
97 MOJO_CHECK(max_num_bytes);
98 MOJO_CHECK(error);
99 MOJO_CHECK(data);
100 MOJO_CHECK(!callback.is_null());
101
102 // This shouldn't be called while a callback is pending.
103 MOJO_CHECK(callback_.is_null());
104
105 got_request_data_ = true;
106 QuitMessageLoop();
107
108 if (!complete_synchronously_) {
109 callback_ = callback;
110 return false;
111 }
112
113 *error = mojo::files::Error::OK;
114 *data = data_.Clone();
115 return true;
116 }
117 void OnClosed() override {
118 got_on_closed_ = true;
119 QuitMessageLoop();
120 }
121
122 mojo::Array<uint8_t> data_;
123 bool complete_synchronously_;
124
125 bool got_request_data_;
126 bool got_on_closed_;
127
128 RequestDataCallback callback_;
129
130 MOJO_DISALLOW_COPY_AND_ASSIGN(TestClient);
131 };
132
133 void TestReadSync(mojo::files::File* file,
134 TestClient* client,
135 const std::string& s) {
136 bool read_cb_called = false;
137 mojo::files::Error error = mojo::files::Error::INTERNAL;
138 mojo::Array<uint8_t> data;
139 file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT,
140 [&read_cb_called, &error, &data](mojo::files::Error e,
141 mojo::Array<uint8_t> d) {
142 read_cb_called = true;
143 error = e;
144 data = d.Pass();
145 QuitMessageLoop();
146 });
147 if (client) {
148 // If there's a client, since we're running everything on one thread, the
149 // impl (which will call the client, which will quit the message loop) will
150 // get called before the callback.
151 client->Reset();
152 client->set_data(StringToArray(s));
153 RunMessageLoop();
154 EXPECT_TRUE(client->got_request_data());
155 EXPECT_FALSE(client->got_on_closed());
156 EXPECT_FALSE(read_cb_called);
157 // Spin the message loop again to get the callback.
158 client->Reset();
159 RunMessageLoop();
160 EXPECT_FALSE(client->got_request_data());
161 EXPECT_FALSE(client->got_on_closed());
162 EXPECT_TRUE(read_cb_called);
163 EXPECT_EQ(mojo::files::Error::OK, error);
164 EXPECT_EQ(s, ArrayToString(data));
165 } else {
166 // Otherwise, only the read callback will be called and quit the message
167 // loop.
168 RunMessageLoop();
169 EXPECT_TRUE(read_cb_called);
170 EXPECT_EQ(mojo::files::Error::UNAVAILABLE, error);
171 EXPECT_TRUE(data.is_null());
172 }
173 }
174
175 void TestReadAsync(mojo::files::File* file,
176 TestClient* client,
177 const std::string& s) {
178 MOJO_CHECK(client);
179
180 bool read_cb_called = false;
181 mojo::files::Error error = mojo::files::Error::INTERNAL;
182 mojo::Array<uint8_t> data;
183 file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT,
184 [&read_cb_called, &error, &data](mojo::files::Error e,
185 mojo::Array<uint8_t> d) {
186 read_cb_called = true;
187 error = e;
188 data = d.Pass();
189 QuitMessageLoop();
190 });
191 client->Reset();
192 client->set_complete_synchronously(false);
193 RunMessageLoop();
194 EXPECT_TRUE(client->got_request_data());
195 EXPECT_FALSE(client->got_on_closed());
196 EXPECT_FALSE(read_cb_called);
197 // The read callback won't get called until we tell the client to run its
198 // callback.
199 client->Reset();
200 client->RunRequestDataCallback(StringToArray(s));
201 EXPECT_FALSE(client->got_request_data());
202 EXPECT_FALSE(client->got_on_closed());
203 EXPECT_FALSE(read_cb_called);
204 // Spin the message loop again to get the read callback.
205 client->Reset();
206 RunMessageLoop();
207 EXPECT_FALSE(client->got_request_data());
208 EXPECT_FALSE(client->got_on_closed());
209 EXPECT_TRUE(read_cb_called);
210 EXPECT_EQ(mojo::files::Error::OK, error);
211 EXPECT_EQ(s, ArrayToString(data));
212 }
213
214 void TestClose(mojo::files::File* file, TestClient* client) {
215 bool close_cb_called = false;
216 mojo::files::Error error = mojo::files::Error::INTERNAL;
217 file->Close([&close_cb_called, &error](mojo::files::Error e) {
218 close_cb_called = true;
219 error = e;
220 QuitMessageLoop();
221 });
222 if (client) {
223 // (This is analogous to |TestReadSync()|.)
224 client->Reset();
225 RunMessageLoop();
226 EXPECT_FALSE(client->got_request_data());
227 EXPECT_TRUE(client->got_on_closed());
228 EXPECT_FALSE(close_cb_called);
229 client->Reset();
230 RunMessageLoop();
231 EXPECT_FALSE(client->got_request_data());
232 EXPECT_FALSE(client->got_on_closed());
233 } else {
234 RunMessageLoop();
235 }
236 EXPECT_TRUE(close_cb_called);
237 EXPECT_EQ(mojo::files::Error::OK, error);
238 }
239
240 TEST_F(InputStreamFileTest, BasicSync) {
241 mojo::files::FilePtr file;
242 TestClient client;
243 std::unique_ptr<InputStreamFile> file_impl =
244 InputStreamFile::Create(&client, GetProxy(&file));
245
246 TestReadSync(file.get(), &client, "hello");
247 TestReadSync(file.get(), &client, "world");
248 TestClose(file.get(), &client);
249 }
250
251 TEST_F(InputStreamFileTest, BasicAsync) {
252 mojo::files::FilePtr file;
253 TestClient client;
254 std::unique_ptr<InputStreamFile> file_impl =
255 InputStreamFile::Create(&client, GetProxy(&file));
256
257 TestReadAsync(file.get(), &client, "hello");
258 TestReadAsync(file.get(), &client, "world");
259 TestClose(file.get(), &client);
260 }
261
262 TEST_F(InputStreamFileTest, SetClient) {
263 mojo::files::FilePtr file;
264 TestClient client1;
265 std::unique_ptr<InputStreamFile> file_impl =
266 InputStreamFile::Create(&client1, GetProxy(&file));
267
268 TestReadSync(file.get(), &client1, "hello");
269
270 TestClient client2;
271 file_impl->set_client(&client2);
272 TestReadSync(file.get(), &client2, "world");
273
274 file_impl->set_client(&client1);
275 TestReadAsync(file.get(), &client1, "!");
276 TestClose(file.get(), &client1);
277 }
278
279 TEST_F(InputStreamFileTest, NullClient) {
280 mojo::files::FilePtr file;
281 std::unique_ptr<InputStreamFile> file_impl =
282 InputStreamFile::Create(nullptr, GetProxy(&file));
283
284 TestReadSync(file.get(), nullptr, "hello");
285 TestClose(file.get(), nullptr);
286 }
287
288 TEST_F(InputStreamFileTest, SetNullClient) {
289 mojo::files::FilePtr file;
290 TestClient client;
291 std::unique_ptr<InputStreamFile> file_impl =
292 InputStreamFile::Create(&client, GetProxy(&file));
293
294 TestReadSync(file.get(), &client, "hello");
295
296 file_impl->set_client(nullptr);
297 client.Reset();
298 TestReadSync(file.get(), nullptr, "hello");
299 TestClose(file.get(), nullptr);
300 EXPECT_FALSE(client.got_request_data());
301 EXPECT_FALSE(client.got_on_closed());
302 }
303
304 TEST_F(InputStreamFileTest, ImplOnlyClosesMessagePipeOnDestruction) {
305 mojo::files::FilePtr file;
306 std::unique_ptr<InputStreamFile> file_impl =
307 InputStreamFile::Create(nullptr, GetProxy(&file));
308 bool got_connection_error = false;
309 file.set_connection_error_handler([&got_connection_error]() {
310 got_connection_error = true;
311 QuitMessageLoop();
312 });
313
314 TestClose(file.get(), nullptr);
315 // The impl should only close its end when it's destroyed (even if |Close()|
316 // has been called).
317 RunMessageLoopUntilIdle();
318 EXPECT_FALSE(got_connection_error);
319 file_impl.reset();
320 RunMessageLoop();
321 EXPECT_TRUE(got_connection_error);
322 }
323
324 TEST_F(InputStreamFileTest, ClosingMessagePipeCausesOnClosed) {
325 mojo::files::FilePtr file;
326 TestClient client;
327 std::unique_ptr<InputStreamFile> file_impl =
328 InputStreamFile::Create(&client, GetProxy(&file));
329
330 file.reset();
331 RunMessageLoop();
332 EXPECT_FALSE(client.got_request_data());
333 EXPECT_TRUE(client.got_on_closed());
334 }
335
336 // Clients may own the impl (and this is a typical pattern). This client will
337 // own/destroy its impl on any |Client| call (and we'll test that this doesn't
338 // result in any additional calls to the client).
339 class TestClientDestroysImplClient : public InputStreamFile::Client {
340 public:
341 explicit TestClientDestroysImplClient(
342 mojo::InterfaceRequest<mojo::files::File> request)
343 : file_impl_(InputStreamFile::Create(this, request.Pass())) {}
344 ~TestClientDestroysImplClient() override {}
345
346 private:
347 // InputStreamFile::Client|:
348 bool RequestData(size_t /*max_num_bytes*/,
349 mojo::files::Error* /*error*/,
350 mojo::Array<uint8_t>* /*data*/,
351 const RequestDataCallback& /*callback*/) override {
352 // We reset the impl on any call, and afterwards it shouldn't call us.
353 EXPECT_TRUE(file_impl_);
354 file_impl_.reset();
355 return true;
356 }
357 void OnClosed() override {
358 // We reset the impl on any call, and afterwards it shouldn't call us.
359 EXPECT_TRUE(file_impl_);
360 file_impl_.reset();
361 }
362
363 std::unique_ptr<InputStreamFile> file_impl_;
364
365 MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientDestroysImplClient);
366 };
367
368 TEST_F(InputStreamFileTest, ClientDestroysImpl) {
369 // Test destruction due to reading.
370 {
371 mojo::files::FilePtr file;
372 TestClientDestroysImplClient client(GetProxy(&file));
373 bool got_connection_error = false;
374 file.set_connection_error_handler([&got_connection_error]() {
375 got_connection_error = true;
376 QuitMessageLoop();
377 });
378 // If the impl is destroyed while trying to answer a read, it doesn't
379 // respond.
380 // TODO(vtl): I'm not sure if this is the best behavior. Maybe it should
381 // respond with an error?
382 file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT,
383 [](mojo::files::Error, mojo::Array<uint8_t>) {
384 MOJO_CHECK(false) << "Not reached";
385 });
386 RunMessageLoop();
387 EXPECT_TRUE(got_connection_error);
388 }
389
390 // Test destruction due to closing.
391 {
392 mojo::files::FilePtr file;
393 TestClientDestroysImplClient client(GetProxy(&file));
394 bool got_connection_error = false;
395 file.set_connection_error_handler([&got_connection_error]() {
396 got_connection_error = true;
397 QuitMessageLoop();
398 });
399 TestClose(file.get(), nullptr);
400 if (!got_connection_error)
401 RunMessageLoop();
402 EXPECT_TRUE(got_connection_error);
403 }
404 }
405
406 // This responds synchronously to any (non-zero-byte) read with a single byte,
407 // starting with 0 and incrementing each time.
408 class TestClientFifoSync : public InputStreamFile::Client {
409 public:
410 explicit TestClientFifoSync(mojo::InterfaceRequest<mojo::files::File> request)
411 : file_impl_(InputStreamFile::Create(this, request.Pass())),
412 next_byte_(0u) {}
413 ~TestClientFifoSync() override {}
414
415 private:
416 // InputStreamFile::Client|:
417 bool RequestData(size_t max_num_bytes,
418 mojo::files::Error* error,
419 mojo::Array<uint8_t>* data,
420 const RequestDataCallback& callback) override {
421 *error = mojo::files::Error::OK;
422 data->resize(1);
423 (*data)[0] = next_byte_++;
424 return true;
425 }
426 void OnClosed() override {}
427
428 std::unique_ptr<InputStreamFile> file_impl_;
429 uint8_t next_byte_;
430
431 MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientFifoSync);
432 };
433
434 // Like |TestClientFifoSync|, but asynchronous.
435 class TestClientFifoAsync : public InputStreamFile::Client {
436 public:
437 explicit TestClientFifoAsync(
438 mojo::InterfaceRequest<mojo::files::File> request)
439 : file_impl_(InputStreamFile::Create(this, request.Pass())),
440 next_byte_(0u) {}
441 ~TestClientFifoAsync() override {}
442
443 private:
444 // InputStreamFile::Client|:
445 bool RequestData(size_t max_num_bytes,
446 mojo::files::Error* error,
447 mojo::Array<uint8_t>* data,
448 const RequestDataCallback& callback) override {
449 PostTaskToMessageLoop([this, callback]() {
450 mojo::Array<uint8_t> data;
451 data.push_back(next_byte_++);
452 callback.Run(mojo::files::Error::OK, data.Pass());
453 });
454 return false;
455 }
456 void OnClosed() override {}
457
458 std::unique_ptr<InputStreamFile> file_impl_;
459 uint8_t next_byte_;
460
461 MOJO_DISALLOW_COPY_AND_ASSIGN(TestClientFifoAsync);
462 };
463
464 void TestFifo(mojo::files::File* file) {
465 int expect_callback = 0;
466 uint8_t expect_byte = 0;
467
468 // Test a couple of zero-byte reads.
469 file->Read(0u, 0, mojo::files::Whence::FROM_CURRENT,
470 [&expect_callback](mojo::files::Error e, mojo::Array<uint8_t> a) {
471 EXPECT_EQ(0, expect_callback++);
472 EXPECT_EQ(mojo::files::Error::OK, e);
473 EXPECT_EQ(0u, a.size());
474 });
475 file->Read(0u, 0, mojo::files::Whence::FROM_CURRENT,
476 [&expect_callback](mojo::files::Error e, mojo::Array<uint8_t> a) {
477 EXPECT_EQ(1, expect_callback++);
478 EXPECT_EQ(mojo::files::Error::OK, e);
479 EXPECT_EQ(0u, a.size());
480 });
481
482 // Test a couple of non-zero-byte reads.
483 file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT,
484 [&expect_callback, &expect_byte](mojo::files::Error e,
485 mojo::Array<uint8_t> a) {
486 EXPECT_EQ(2, expect_callback++);
487 EXPECT_EQ(mojo::files::Error::OK, e);
488 EXPECT_EQ(1u, a.size());
489 EXPECT_EQ(expect_byte++, a[0]);
490 });
491 file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT,
492 [&expect_callback, &expect_byte](mojo::files::Error e,
493 mojo::Array<uint8_t> a) {
494 EXPECT_EQ(3, expect_callback++);
495 EXPECT_EQ(mojo::files::Error::OK, e);
496 EXPECT_EQ(1u, a.size());
497 EXPECT_EQ(expect_byte++, a[0]);
498 });
499 // Throw in a zero-byte read.
500 file->Read(0u, 0, mojo::files::Whence::FROM_CURRENT,
501 [&expect_callback](mojo::files::Error e, mojo::Array<uint8_t> a) {
502 EXPECT_EQ(4, expect_callback++);
503 EXPECT_EQ(mojo::files::Error::OK, e);
504 EXPECT_EQ(0u, a.size());
505 });
506 // And a final non-zero-byte read (we'll quit the message loop).
507 file->Read(100u, 0, mojo::files::Whence::FROM_CURRENT,
508 [&expect_callback, &expect_byte](mojo::files::Error e,
509 mojo::Array<uint8_t> a) {
510 EXPECT_EQ(5, expect_callback++);
511 EXPECT_EQ(mojo::files::Error::OK, e);
512 EXPECT_EQ(1u, a.size());
513 EXPECT_EQ(expect_byte++, a[0]);
514 QuitMessageLoop();
515 });
516 RunMessageLoop();
517
518 // Check that all the callbacks ran.
519 EXPECT_EQ(6, expect_callback);
520 EXPECT_EQ(3u, expect_byte);
521 }
522
523 // Tests that if multiple reads are sent, they are completed in FIFO order. This
524 // tests the synchronous completion path.
525 TEST_F(InputStreamFileTest, FifoSync) {
526 mojo::files::FilePtr file;
527 TestClientFifoSync client(GetProxy(&file));
528 TestFifo(file.get());
529 }
530
531 // Like |InputStreamFileTest.FifoSync|, but asynchronous.
532 TEST_F(InputStreamFileTest, FifoAsync) {
533 mojo::files::FilePtr file;
534 TestClientFifoAsync client(GetProxy(&file));
535 TestFifo(file.get());
536 }
537
538 } // namespace
539 } // namespace files_impl
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698