• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium OS 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 <brillo/streams/stream.h>
6 
7 #include <limits>
8 
9 #include <base/callback.h>
10 #include <gmock/gmock.h>
11 #include <gtest/gtest.h>
12 
13 #include <brillo/bind_lambda.h>
14 #include <brillo/message_loops/fake_message_loop.h>
15 #include <brillo/streams/stream_errors.h>
16 
17 using testing::DoAll;
18 using testing::InSequence;
19 using testing::Return;
20 using testing::SaveArg;
21 using testing::SetArgPointee;
22 using testing::_;
23 
24 namespace {
25 
26 // Helper function for base::Bind.
SetToTrue(bool * target,const brillo::Error *)27 void SetToTrue(bool* target, const brillo::Error* /* error */) {
28   *target = true;
29 }
30 
31 }  // namespace
32 
33 namespace brillo {
34 
35 using AccessMode = Stream::AccessMode;
36 using Whence = Stream::Whence;
37 
38 // To verify "non-trivial" methods implemented in Stream, mock out the
39 // "trivial" methods to make sure the ones we are interested in testing
40 // actually end up calling the expected methods with right parameters.
41 class MockStreamImpl : public Stream {
42  public:
43   MockStreamImpl() = default;
44 
45   MOCK_CONST_METHOD0(IsOpen, bool());
46   MOCK_CONST_METHOD0(CanRead, bool());
47   MOCK_CONST_METHOD0(CanWrite, bool());
48   MOCK_CONST_METHOD0(CanSeek, bool());
49   MOCK_CONST_METHOD0(CanGetSize, bool());
50 
51   MOCK_CONST_METHOD0(GetSize, uint64_t());
52   MOCK_METHOD2(SetSizeBlocking, bool(uint64_t, ErrorPtr*));
53   MOCK_CONST_METHOD0(GetRemainingSize, uint64_t());
54 
55   MOCK_CONST_METHOD0(GetPosition, uint64_t());
56   MOCK_METHOD4(Seek, bool(int64_t, Whence, uint64_t*, ErrorPtr*));
57 
58   // Omitted: ReadAsync
59   // Omitted: ReadAllAsync
60   MOCK_METHOD5(ReadNonBlocking, bool(void*, size_t, size_t*, bool*, ErrorPtr*));
61   // Omitted: ReadBlocking
62   // Omitted: ReadAllBlocking
63 
64   // Omitted: WriteAsync
65   // Omitted: WriteAllAsync
66   MOCK_METHOD4(WriteNonBlocking, bool(const void*, size_t, size_t*, ErrorPtr*));
67   // Omitted: WriteBlocking
68   // Omitted: WriteAllBlocking
69 
70   MOCK_METHOD1(FlushBlocking, bool(ErrorPtr*));
71   MOCK_METHOD1(CloseBlocking, bool(ErrorPtr*));
72 
73   MOCK_METHOD3(WaitForData, bool(AccessMode,
74                                  const base::Callback<void(AccessMode)>&,
75                                  ErrorPtr*));
76   MOCK_METHOD4(WaitForDataBlocking,
77                bool(AccessMode, base::TimeDelta, AccessMode*, ErrorPtr*));
78 
79  private:
80   DISALLOW_COPY_AND_ASSIGN(MockStreamImpl);
81 };
82 
TEST(Stream,TruncateBlocking)83 TEST(Stream, TruncateBlocking) {
84   MockStreamImpl stream_mock;
85   EXPECT_CALL(stream_mock, GetPosition()).WillOnce(Return(123));
86   EXPECT_CALL(stream_mock, SetSizeBlocking(123, _)).WillOnce(Return(true));
87   EXPECT_TRUE(stream_mock.TruncateBlocking(nullptr));
88 }
89 
TEST(Stream,SetPosition)90 TEST(Stream, SetPosition) {
91   MockStreamImpl stream_mock;
92   EXPECT_CALL(stream_mock, Seek(12345, Whence::FROM_BEGIN, _, _))
93       .WillOnce(Return(true));
94   EXPECT_TRUE(stream_mock.SetPosition(12345, nullptr));
95 
96   // Test too large an offset (that doesn't fit in signed 64 bit value).
97   ErrorPtr error;
98   uint64_t max_offset = std::numeric_limits<int64_t>::max();
99   EXPECT_CALL(stream_mock, Seek(max_offset, _, _, _))
100       .WillOnce(Return(true));
101   EXPECT_TRUE(stream_mock.SetPosition(max_offset, nullptr));
102 
103   EXPECT_FALSE(stream_mock.SetPosition(max_offset + 1, &error));
104   EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
105   EXPECT_EQ(errors::stream::kInvalidParameter, error->GetCode());
106 }
107 
TEST(Stream,ReadAsync)108 TEST(Stream, ReadAsync) {
109   size_t read_size = 0;
110   bool succeeded = false;
111   bool failed = false;
112   auto success_callback = base::Bind(
113       [](size_t* read_size, bool* succeeded, size_t size) {
114     *read_size = size;
115     *succeeded = true;
116   }, &read_size, &succeeded);
117   auto error_callback = base::Bind(&SetToTrue, &failed);
118 
119   MockStreamImpl stream_mock;
120   base::Callback<void(AccessMode)> data_callback;
121   char buf[10];
122 
123   // This sets up an initial non blocking read that would block, so ReadAsync()
124   // should wait for more data.
125   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
126       .WillOnce(
127           DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true)));
128   EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _))
129       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
130   EXPECT_TRUE(stream_mock.ReadAsync(buf, sizeof(buf), success_callback,
131                                     error_callback, nullptr));
132   EXPECT_EQ(0u, read_size);
133   EXPECT_FALSE(succeeded);
134   EXPECT_FALSE(failed);
135 
136   // Since the previous call is waiting for the data to be available, we can't
137   // schedule another read.
138   ErrorPtr error;
139   EXPECT_FALSE(stream_mock.ReadAsync(buf, sizeof(buf), success_callback,
140                                      error_callback, &error));
141   EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
142   EXPECT_EQ(errors::stream::kOperationNotSupported, error->GetCode());
143   EXPECT_EQ("Another asynchronous operation is still pending",
144             error->GetMessage());
145 
146   // Making the data available via data_callback should not schedule the
147   // success callback from the main loop and run it directly instead.
148   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
149       .WillOnce(DoAll(SetArgPointee<2>(7),
150                       SetArgPointee<3>(false),
151                       Return(true)));
152   data_callback.Run(AccessMode::READ);
153   EXPECT_EQ(7u, read_size);
154   EXPECT_FALSE(failed);
155 }
156 
TEST(Stream,ReadAsync_DontWaitForData)157 TEST(Stream, ReadAsync_DontWaitForData) {
158   bool succeeded = false;
159   bool failed = false;
160   auto success_callback = base::Bind([](bool* succeeded, size_t /* size */) {
161     *succeeded = true;
162   }, &succeeded);
163   auto error_callback = base::Bind(&SetToTrue, &failed);
164 
165   MockStreamImpl stream_mock;
166   char buf[10];
167   FakeMessageLoop fake_loop_{nullptr};
168   fake_loop_.SetAsCurrent();
169 
170   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
171       .WillOnce(
172           DoAll(SetArgPointee<2>(5), SetArgPointee<3>(false), Return(true)));
173   EXPECT_CALL(stream_mock, WaitForData(_, _, _)).Times(0);
174   EXPECT_TRUE(stream_mock.ReadAsync(buf, sizeof(buf), success_callback,
175                                     error_callback, nullptr));
176   // Even if ReadNonBlocking() returned some data without waiting, the
177   // |success_callback| should not run yet.
178   EXPECT_TRUE(fake_loop_.PendingTasks());
179   EXPECT_FALSE(succeeded);
180   EXPECT_FALSE(failed);
181 
182   // Since the previous callback is still waiting in the main loop, we can't
183   // schedule another read yet.
184   ErrorPtr error;
185   EXPECT_FALSE(stream_mock.ReadAsync(buf, sizeof(buf), success_callback,
186                                      error_callback, &error));
187   EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
188   EXPECT_EQ(errors::stream::kOperationNotSupported, error->GetCode());
189   EXPECT_EQ("Another asynchronous operation is still pending",
190             error->GetMessage());
191 
192   fake_loop_.Run();
193   EXPECT_TRUE(succeeded);
194   EXPECT_FALSE(failed);
195 }
196 
TEST(Stream,ReadAllAsync)197 TEST(Stream, ReadAllAsync) {
198   bool succeeded = false;
199   bool failed = false;
200   auto success_callback = base::Bind([](bool* succeeded) { *succeeded = true; },
201                                      &succeeded);
202   auto error_callback = base::Bind(&SetToTrue, &failed);
203 
204   MockStreamImpl stream_mock;
205   base::Callback<void(AccessMode)> data_callback;
206   char buf[10];
207 
208   // This sets up an initial non blocking read that would block, so
209   // ReadAllAsync() should wait for more data.
210   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
211       .WillOnce(
212           DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true)));
213   EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _))
214       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
215   EXPECT_TRUE(stream_mock.ReadAllAsync(buf, sizeof(buf), success_callback,
216                                        error_callback, nullptr));
217   EXPECT_FALSE(succeeded);
218   EXPECT_FALSE(failed);
219   testing::Mock::VerifyAndClearExpectations(&stream_mock);
220 
221   // ReadAllAsync() will try to read non blocking until the read would block
222   // before it waits for the data to be available again.
223   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
224       .WillOnce(DoAll(SetArgPointee<2>(7),
225                       SetArgPointee<3>(false),
226                       Return(true)));
227   EXPECT_CALL(stream_mock, ReadNonBlocking(buf + 7, 3, _, _, _))
228       .WillOnce(
229           DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true)));
230   EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _))
231       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
232   data_callback.Run(AccessMode::READ);
233   EXPECT_FALSE(succeeded);
234   EXPECT_FALSE(failed);
235   testing::Mock::VerifyAndClearExpectations(&stream_mock);
236 
237   EXPECT_CALL(stream_mock, ReadNonBlocking(buf + 7, 3, _, _, _))
238       .WillOnce(DoAll(SetArgPointee<2>(3),
239                       SetArgPointee<3>(true),
240                       Return(true)));
241   data_callback.Run(AccessMode::READ);
242   EXPECT_TRUE(succeeded);
243   EXPECT_FALSE(failed);
244 }
245 
TEST(Stream,ReadAllAsync_EOS)246 TEST(Stream, ReadAllAsync_EOS) {
247   bool succeeded = false;
248   bool failed = false;
249   auto success_callback = base::Bind([](bool* succeeded) { *succeeded = true; },
250                                      &succeeded);
251   auto error_callback = base::Bind([](bool* failed, const Error* error) {
252     ASSERT_EQ(errors::stream::kDomain, error->GetDomain());
253     ASSERT_EQ(errors::stream::kPartialData, error->GetCode());
254     *failed = true;
255   }, &failed);
256 
257   MockStreamImpl stream_mock;
258   base::Callback<void(AccessMode)> data_callback;
259   char buf[10];
260 
261   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
262       .WillOnce(
263           DoAll(SetArgPointee<2>(0), SetArgPointee<3>(false), Return(true)));
264   EXPECT_CALL(stream_mock, WaitForData(AccessMode::READ, _, _))
265       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
266   EXPECT_TRUE(stream_mock.ReadAllAsync(buf, sizeof(buf), success_callback,
267                                        error_callback, nullptr));
268 
269   // ReadAsyncAll() should finish and fail once ReadNonBlocking() returns an
270   // end-of-stream condition.
271   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 10, _, _, _))
272       .WillOnce(DoAll(SetArgPointee<2>(7),
273                       SetArgPointee<3>(true),
274                       Return(true)));
275   data_callback.Run(AccessMode::READ);
276   EXPECT_FALSE(succeeded);
277   EXPECT_TRUE(failed);
278 }
279 
TEST(Stream,ReadBlocking)280 TEST(Stream, ReadBlocking) {
281   MockStreamImpl stream_mock;
282   char buf[1024];
283   size_t read = 0;
284 
285   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
286       .WillOnce(DoAll(SetArgPointee<2>(24),
287                       SetArgPointee<3>(false),
288                       Return(true)));
289   EXPECT_TRUE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr));
290   EXPECT_EQ(24, read);
291 
292   EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
293       .WillOnce(DoAll(SetArgPointee<2>(0),
294                       SetArgPointee<3>(true),
295                       Return(true)));
296   EXPECT_TRUE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr));
297   EXPECT_EQ(0, read);
298 
299   {
300     InSequence seq;
301     EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
302         .WillOnce(DoAll(SetArgPointee<2>(0),
303                         SetArgPointee<3>(false),
304                         Return(true)));
305     EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _))
306         .WillOnce(Return(true));
307     EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
308         .WillOnce(DoAll(SetArgPointee<2>(0),
309                         SetArgPointee<3>(false),
310                         Return(true)));
311     EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _))
312         .WillOnce(Return(true));
313     EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
314         .WillOnce(DoAll(SetArgPointee<2>(124),
315                         SetArgPointee<3>(false),
316                         Return(true)));
317   }
318   EXPECT_TRUE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr));
319   EXPECT_EQ(124, read);
320 
321   {
322     InSequence seq;
323     EXPECT_CALL(stream_mock, ReadNonBlocking(buf, 1024, _, _, _))
324         .WillOnce(DoAll(SetArgPointee<2>(0),
325                         SetArgPointee<3>(false),
326                         Return(true)));
327     EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::READ, _, _, _))
328         .WillOnce(Return(false));
329   }
330   EXPECT_FALSE(stream_mock.ReadBlocking(buf, sizeof(buf), &read, nullptr));
331 }
332 
TEST(Stream,ReadAllBlocking)333 TEST(Stream, ReadAllBlocking) {
334   class MockReadBlocking : public MockStreamImpl {
335    public:
336     MOCK_METHOD4(ReadBlocking, bool(void*, size_t, size_t*, ErrorPtr*));
337   } stream_mock;
338 
339   char buf[1024];
340 
341   EXPECT_CALL(stream_mock, ReadBlocking(buf, 1024, _, _))
342       .WillOnce(DoAll(SetArgPointee<2>(24), Return(true)));
343   EXPECT_CALL(stream_mock, ReadBlocking(buf + 24, 1000, _, _))
344       .WillOnce(DoAll(SetArgPointee<2>(1000), Return(true)));
345   EXPECT_TRUE(stream_mock.ReadAllBlocking(buf, sizeof(buf), nullptr));
346 
347   ErrorPtr error;
348   EXPECT_CALL(stream_mock, ReadBlocking(buf, 1024, _, _))
349       .WillOnce(DoAll(SetArgPointee<2>(24), Return(true)));
350   EXPECT_CALL(stream_mock, ReadBlocking(buf + 24, 1000, _, _))
351       .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
352   EXPECT_FALSE(stream_mock.ReadAllBlocking(buf, sizeof(buf), &error));
353   EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
354   EXPECT_EQ(errors::stream::kPartialData, error->GetCode());
355 }
356 
TEST(Stream,WriteAsync)357 TEST(Stream, WriteAsync) {
358   size_t write_size = 0;
359   bool failed = false;
360   auto success_callback = base::Bind([](size_t* write_size, size_t size) {
361     *write_size = size;
362   }, &write_size);
363   auto error_callback = base::Bind(&SetToTrue, &failed);
364 
365   MockStreamImpl stream_mock;
366   InSequence s;
367   base::Callback<void(AccessMode)> data_callback;
368   char buf[10] = {};
369 
370   // WriteNonBlocking returns a blocking situation (size_written = 0) so the
371   // WaitForData() is run.
372   EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _))
373       .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
374   EXPECT_CALL(stream_mock, WaitForData(AccessMode::WRITE, _, _))
375       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
376   EXPECT_TRUE(stream_mock.WriteAsync(buf, sizeof(buf), success_callback,
377                                      error_callback, nullptr));
378   EXPECT_EQ(0u, write_size);
379   EXPECT_FALSE(failed);
380 
381   ErrorPtr error;
382   EXPECT_FALSE(stream_mock.WriteAsync(buf, sizeof(buf), success_callback,
383                                       error_callback, &error));
384   EXPECT_EQ(errors::stream::kDomain, error->GetDomain());
385   EXPECT_EQ(errors::stream::kOperationNotSupported, error->GetCode());
386   EXPECT_EQ("Another asynchronous operation is still pending",
387             error->GetMessage());
388 
389   EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _))
390       .WillOnce(DoAll(SetArgPointee<2>(7), Return(true)));
391   data_callback.Run(AccessMode::WRITE);
392   EXPECT_EQ(7u, write_size);
393   EXPECT_FALSE(failed);
394 }
395 
TEST(Stream,WriteAllAsync)396 TEST(Stream, WriteAllAsync) {
397   bool succeeded = false;
398   bool failed = false;
399   auto success_callback = base::Bind([](bool* succeeded) { *succeeded = true; },
400                                      &succeeded);
401   auto error_callback = base::Bind(&SetToTrue, &failed);
402 
403   MockStreamImpl stream_mock;
404   base::Callback<void(AccessMode)> data_callback;
405   char buf[10] = {};
406 
407   EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _))
408       .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
409   EXPECT_CALL(stream_mock, WaitForData(AccessMode::WRITE, _, _))
410       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
411   EXPECT_TRUE(stream_mock.WriteAllAsync(buf, sizeof(buf), success_callback,
412                                         error_callback, nullptr));
413   testing::Mock::VerifyAndClearExpectations(&stream_mock);
414   EXPECT_FALSE(succeeded);
415   EXPECT_FALSE(failed);
416 
417   EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 10, _, _))
418       .WillOnce(DoAll(SetArgPointee<2>(7), Return(true)));
419   EXPECT_CALL(stream_mock, WriteNonBlocking(buf + 7, 3, _, _))
420       .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
421   EXPECT_CALL(stream_mock, WaitForData(AccessMode::WRITE, _, _))
422       .WillOnce(DoAll(SaveArg<1>(&data_callback), Return(true)));
423   data_callback.Run(AccessMode::WRITE);
424   testing::Mock::VerifyAndClearExpectations(&stream_mock);
425   EXPECT_FALSE(succeeded);
426   EXPECT_FALSE(failed);
427 
428   EXPECT_CALL(stream_mock, WriteNonBlocking(buf + 7, 3, _, _))
429       .WillOnce(DoAll(SetArgPointee<2>(3), Return(true)));
430   data_callback.Run(AccessMode::WRITE);
431   EXPECT_TRUE(succeeded);
432   EXPECT_FALSE(failed);
433 }
434 
TEST(Stream,WriteBlocking)435 TEST(Stream, WriteBlocking) {
436   MockStreamImpl stream_mock;
437   char buf[1024];
438   size_t written = 0;
439 
440   EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
441       .WillOnce(DoAll(SetArgPointee<2>(24), Return(true)));
442   EXPECT_TRUE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr));
443   EXPECT_EQ(24, written);
444 
445   {
446     InSequence seq;
447     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
448           .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
449     EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _))
450         .WillOnce(Return(true));
451     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
452           .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
453     EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _))
454         .WillOnce(Return(true));
455     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
456           .WillOnce(DoAll(SetArgPointee<2>(124), Return(true)));
457   }
458   EXPECT_TRUE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr));
459   EXPECT_EQ(124, written);
460 
461   {
462     InSequence seq;
463     EXPECT_CALL(stream_mock, WriteNonBlocking(buf, 1024, _, _))
464           .WillOnce(DoAll(SetArgPointee<2>(0), Return(true)));
465     EXPECT_CALL(stream_mock, WaitForDataBlocking(AccessMode::WRITE, _, _, _))
466         .WillOnce(Return(false));
467   }
468   EXPECT_FALSE(stream_mock.WriteBlocking(buf, sizeof(buf), &written, nullptr));
469 }
470 
TEST(Stream,WriteAllBlocking)471 TEST(Stream, WriteAllBlocking) {
472   class MockWritelocking : public MockStreamImpl {
473    public:
474     MOCK_METHOD4(WriteBlocking, bool(const void*, size_t, size_t*, ErrorPtr*));
475   } stream_mock;
476 
477   char buf[1024];
478 
479   EXPECT_CALL(stream_mock, WriteBlocking(buf, 1024, _, _))
480       .WillOnce(DoAll(SetArgPointee<2>(24), Return(true)));
481   EXPECT_CALL(stream_mock, WriteBlocking(buf + 24, 1000, _, _))
482       .WillOnce(DoAll(SetArgPointee<2>(1000), Return(true)));
483   EXPECT_TRUE(stream_mock.WriteAllBlocking(buf, sizeof(buf), nullptr));
484 }
485 
486 }  // namespace brillo
487