• 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/message_loops/message_loop.h>
6 
7 // These are the common tests for all the brillo::MessageLoop implementations
8 // that should conform to this interface's contracts. For extra
9 // implementation-specific tests see the particular implementation unittests in
10 // the *_unittest.cc files.
11 
12 #include <memory>
13 #include <vector>
14 
15 #include <base/bind.h>
16 #include <base/location.h>
17 #include <base/posix/eintr_wrapper.h>
18 #include <gtest/gtest.h>
19 
20 #include <brillo/bind_lambda.h>
21 #include <brillo/unittest_utils.h>
22 #include <brillo/message_loops/base_message_loop.h>
23 #include <brillo/message_loops/glib_message_loop.h>
24 #include <brillo/message_loops/message_loop_utils.h>
25 
26 using base::Bind;
27 using base::TimeDelta;
28 
29 namespace brillo {
30 
31 using TaskId = MessageLoop::TaskId;
32 
33 template <typename T>
34 class MessageLoopTest : public ::testing::Test {
35  protected:
SetUp()36   void SetUp() override {
37     MessageLoopSetUp();
38     EXPECT_TRUE(this->loop_.get());
39   }
40 
41   std::unique_ptr<base::MessageLoopForIO> base_loop_;
42 
43   std::unique_ptr<MessageLoop> loop_;
44 
45  private:
46   // These MessageLoopSetUp() methods are used to setup each MessageLoop
47   // according to its constructor requirements.
48   void MessageLoopSetUp();
49 };
50 
51 template <>
MessageLoopSetUp()52 void MessageLoopTest<GlibMessageLoop>::MessageLoopSetUp() {
53   loop_.reset(new GlibMessageLoop());
54 }
55 
56 template <>
MessageLoopSetUp()57 void MessageLoopTest<BaseMessageLoop>::MessageLoopSetUp() {
58   base_loop_.reset(new base::MessageLoopForIO());
59   loop_.reset(new BaseMessageLoop(base::MessageLoopForIO::current()));
60 }
61 
62 // This setups gtest to run each one of the following TYPED_TEST test cases on
63 // on each implementation.
64 typedef ::testing::Types<
65   GlibMessageLoop,
66   BaseMessageLoop> MessageLoopTypes;
67 TYPED_TEST_CASE(MessageLoopTest, MessageLoopTypes);
68 
69 
TYPED_TEST(MessageLoopTest,CancelTaskInvalidValuesTest)70 TYPED_TEST(MessageLoopTest, CancelTaskInvalidValuesTest) {
71   EXPECT_FALSE(this->loop_->CancelTask(MessageLoop::kTaskIdNull));
72   EXPECT_FALSE(this->loop_->CancelTask(1234));
73 }
74 
TYPED_TEST(MessageLoopTest,PostTaskTest)75 TYPED_TEST(MessageLoopTest, PostTaskTest) {
76   bool called = false;
77   TaskId task_id = this->loop_->PostTask(FROM_HERE,
78                                          Bind([&called]() { called = true; }));
79   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
80   MessageLoopRunMaxIterations(this->loop_.get(), 100);
81   EXPECT_TRUE(called);
82 }
83 
84 // Tests that we can cancel tasks right after we schedule them.
TYPED_TEST(MessageLoopTest,PostTaskCancelledTest)85 TYPED_TEST(MessageLoopTest, PostTaskCancelledTest) {
86   bool called = false;
87   TaskId task_id = this->loop_->PostTask(FROM_HERE,
88                                          Bind([&called]() { called = true; }));
89   EXPECT_TRUE(this->loop_->CancelTask(task_id));
90   MessageLoopRunMaxIterations(this->loop_.get(), 100);
91   EXPECT_FALSE(called);
92   // Can't remove a task you already removed.
93   EXPECT_FALSE(this->loop_->CancelTask(task_id));
94 }
95 
TYPED_TEST(MessageLoopTest,PostDelayedTaskRunsEventuallyTest)96 TYPED_TEST(MessageLoopTest, PostDelayedTaskRunsEventuallyTest) {
97   bool called = false;
98   TaskId task_id = this->loop_->PostDelayedTask(
99       FROM_HERE,
100       Bind([&called]() { called = true; }),
101       TimeDelta::FromMilliseconds(50));
102   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
103   MessageLoopRunUntil(this->loop_.get(),
104                       TimeDelta::FromSeconds(10),
105                       Bind([&called]() { return called; }));
106   // Check that the main loop finished before the 10 seconds timeout, so it
107   // finished due to the callback being called and not due to the timeout.
108   EXPECT_TRUE(called);
109 }
110 
111 // Test that you can call the overloaded version of PostDelayedTask from
112 // MessageLoop. This is important because only one of the two methods is
113 // virtual, so you need to unhide the other when overriding the virtual one.
TYPED_TEST(MessageLoopTest,PostDelayedTaskWithoutLocation)114 TYPED_TEST(MessageLoopTest, PostDelayedTaskWithoutLocation) {
115   this->loop_->PostDelayedTask(Bind(&base::DoNothing), TimeDelta());
116   EXPECT_EQ(1, MessageLoopRunMaxIterations(this->loop_.get(), 100));
117 }
118 
TYPED_TEST(MessageLoopTest,WatchForInvalidFD)119 TYPED_TEST(MessageLoopTest, WatchForInvalidFD) {
120   bool called = false;
121   EXPECT_EQ(MessageLoop::kTaskIdNull, this->loop_->WatchFileDescriptor(
122       FROM_HERE, -1, MessageLoop::kWatchRead, true,
123       Bind([&called] { called = true; })));
124   EXPECT_EQ(MessageLoop::kTaskIdNull, this->loop_->WatchFileDescriptor(
125       FROM_HERE, -1, MessageLoop::kWatchWrite, true,
126       Bind([&called] { called = true; })));
127   EXPECT_EQ(0, MessageLoopRunMaxIterations(this->loop_.get(), 100));
128   EXPECT_FALSE(called);
129 }
130 
TYPED_TEST(MessageLoopTest,CancelWatchedFileDescriptor)131 TYPED_TEST(MessageLoopTest, CancelWatchedFileDescriptor) {
132   ScopedPipe pipe;
133   bool called = false;
134   TaskId task_id = this->loop_->WatchFileDescriptor(
135       FROM_HERE, pipe.reader, MessageLoop::kWatchRead, true,
136       Bind([&called] { called = true; }));
137   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
138   // The reader end is blocked because we didn't write anything to the writer
139   // end.
140   EXPECT_EQ(0, MessageLoopRunMaxIterations(this->loop_.get(), 100));
141   EXPECT_FALSE(called);
142   EXPECT_TRUE(this->loop_->CancelTask(task_id));
143 }
144 
145 // When you watch a file descriptor for reading, the guaranties are that a
146 // blocking call to read() on that file descriptor will not block. This should
147 // include the case when the other end of a pipe is closed or the file is empty.
TYPED_TEST(MessageLoopTest,WatchFileDescriptorTriggersWhenPipeClosed)148 TYPED_TEST(MessageLoopTest, WatchFileDescriptorTriggersWhenPipeClosed) {
149   ScopedPipe pipe;
150   bool called = false;
151   EXPECT_EQ(0, HANDLE_EINTR(close(pipe.writer)));
152   pipe.writer = -1;
153   TaskId task_id = this->loop_->WatchFileDescriptor(
154       FROM_HERE, pipe.reader, MessageLoop::kWatchRead, true,
155       Bind([&called] { called = true; }));
156   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
157   // The reader end is not blocked because we closed the writer end so a read on
158   // the reader end would return 0 bytes read.
159   EXPECT_NE(0, MessageLoopRunMaxIterations(this->loop_.get(), 10));
160   EXPECT_TRUE(called);
161   EXPECT_TRUE(this->loop_->CancelTask(task_id));
162 }
163 
164 // When a WatchFileDescriptor task is scheduled with |persistent| = true, we
165 // should keep getting a call whenever the file descriptor is ready.
TYPED_TEST(MessageLoopTest,WatchFileDescriptorPersistently)166 TYPED_TEST(MessageLoopTest, WatchFileDescriptorPersistently) {
167   ScopedPipe pipe;
168   EXPECT_EQ(1, HANDLE_EINTR(write(pipe.writer, "a", 1)));
169 
170   int called = 0;
171   TaskId task_id = this->loop_->WatchFileDescriptor(
172       FROM_HERE, pipe.reader, MessageLoop::kWatchRead, true,
173       Bind([&called] { called++; }));
174   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
175   // We let the main loop run for 20 iterations to give it enough iterations to
176   // verify that our callback was called more than one. We only check that our
177   // callback is called more than once.
178   EXPECT_EQ(20, MessageLoopRunMaxIterations(this->loop_.get(), 20));
179   EXPECT_LT(1, called);
180   EXPECT_TRUE(this->loop_->CancelTask(task_id));
181 }
182 
TYPED_TEST(MessageLoopTest,WatchFileDescriptorNonPersistent)183 TYPED_TEST(MessageLoopTest, WatchFileDescriptorNonPersistent) {
184   ScopedPipe pipe;
185   EXPECT_EQ(1, HANDLE_EINTR(write(pipe.writer, "a", 1)));
186 
187   int called = 0;
188   TaskId task_id = this->loop_->WatchFileDescriptor(
189       FROM_HERE, pipe.reader, MessageLoop::kWatchRead, false,
190       Bind([&called] { called++; }));
191   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
192   // We let the main loop run for 20 iterations but we just expect it to run
193   // at least once. The callback should be called exactly once since we
194   // scheduled it non-persistently. After it ran, we shouldn't be able to cancel
195   // this task.
196   EXPECT_LT(0, MessageLoopRunMaxIterations(this->loop_.get(), 20));
197   EXPECT_EQ(1, called);
198   EXPECT_FALSE(this->loop_->CancelTask(task_id));
199 }
200 
TYPED_TEST(MessageLoopTest,WatchFileDescriptorForReadAndWriteSimultaneously)201 TYPED_TEST(MessageLoopTest, WatchFileDescriptorForReadAndWriteSimultaneously) {
202   ScopedSocketPair socks;
203   EXPECT_EQ(1, HANDLE_EINTR(write(socks.right, "a", 1)));
204   // socks.left should be able to read this "a" and should also be able to write
205   // without blocking since the kernel has some buffering for it.
206 
207   TaskId read_task_id = this->loop_->WatchFileDescriptor(
208       FROM_HERE, socks.left, MessageLoop::kWatchRead, true,
209       Bind([this, &read_task_id] {
210         EXPECT_TRUE(this->loop_->CancelTask(read_task_id))
211             << "task_id" << read_task_id;
212       }));
213   EXPECT_NE(MessageLoop::kTaskIdNull, read_task_id);
214 
215   TaskId write_task_id = this->loop_->WatchFileDescriptor(
216       FROM_HERE, socks.left, MessageLoop::kWatchWrite, true,
217       Bind([this, &write_task_id] {
218         EXPECT_TRUE(this->loop_->CancelTask(write_task_id));
219       }));
220   EXPECT_NE(MessageLoop::kTaskIdNull, write_task_id);
221 
222   EXPECT_LT(0, MessageLoopRunMaxIterations(this->loop_.get(), 20));
223 
224   EXPECT_FALSE(this->loop_->CancelTask(read_task_id));
225   EXPECT_FALSE(this->loop_->CancelTask(write_task_id));
226 }
227 
228 // Test that we can cancel the task we are running, and should just fail.
TYPED_TEST(MessageLoopTest,DeleteTaskFromSelf)229 TYPED_TEST(MessageLoopTest, DeleteTaskFromSelf) {
230   bool cancel_result = true;  // We would expect this to be false.
231   MessageLoop* loop_ptr = this->loop_.get();
232   TaskId task_id;
233   task_id = this->loop_->PostTask(
234       FROM_HERE,
235       Bind([&cancel_result, loop_ptr, &task_id]() {
236         cancel_result = loop_ptr->CancelTask(task_id);
237       }));
238   EXPECT_EQ(1, MessageLoopRunMaxIterations(this->loop_.get(), 100));
239   EXPECT_FALSE(cancel_result);
240 }
241 
242 // Test that we can cancel a non-persistent file descriptor watching callback,
243 // which should fail.
TYPED_TEST(MessageLoopTest,DeleteNonPersistenIOTaskFromSelf)244 TYPED_TEST(MessageLoopTest, DeleteNonPersistenIOTaskFromSelf) {
245   ScopedPipe pipe;
246   TaskId task_id = this->loop_->WatchFileDescriptor(
247       FROM_HERE, pipe.writer, MessageLoop::kWatchWrite, false /* persistent */,
248       Bind([this, &task_id] {
249         EXPECT_FALSE(this->loop_->CancelTask(task_id));
250         task_id = MessageLoop::kTaskIdNull;
251       }));
252   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
253   EXPECT_EQ(1, MessageLoopRunMaxIterations(this->loop_.get(), 100));
254   EXPECT_EQ(MessageLoop::kTaskIdNull, task_id);
255 }
256 
257 // Test that we can cancel a persistent file descriptor watching callback from
258 // the same callback.
TYPED_TEST(MessageLoopTest,DeletePersistenIOTaskFromSelf)259 TYPED_TEST(MessageLoopTest, DeletePersistenIOTaskFromSelf) {
260   ScopedPipe pipe;
261   TaskId task_id = this->loop_->WatchFileDescriptor(
262       FROM_HERE, pipe.writer, MessageLoop::kWatchWrite, true /* persistent */,
263       Bind([this, &task_id] {
264         EXPECT_TRUE(this->loop_->CancelTask(task_id));
265         task_id = MessageLoop::kTaskIdNull;
266       }));
267   EXPECT_NE(MessageLoop::kTaskIdNull, task_id);
268   EXPECT_EQ(1, MessageLoopRunMaxIterations(this->loop_.get(), 100));
269   EXPECT_EQ(MessageLoop::kTaskIdNull, task_id);
270 }
271 
272 // Test that we can cancel several persistent file descriptor watching callbacks
273 // from a scheduled callback. In the BaseMessageLoop implementation, this code
274 // will cause us to cancel an IOTask that has a pending delayed task, but
275 // otherwise is a valid test case on all implementations.
TYPED_TEST(MessageLoopTest,DeleteAllPersistenIOTaskFromSelf)276 TYPED_TEST(MessageLoopTest, DeleteAllPersistenIOTaskFromSelf) {
277   const int kNumTasks = 5;
278   ScopedPipe pipes[kNumTasks];
279   TaskId task_ids[kNumTasks];
280 
281   for (int i = 0; i < kNumTasks; ++i) {
282     task_ids[i] = this->loop_->WatchFileDescriptor(
283         FROM_HERE, pipes[i].writer, MessageLoop::kWatchWrite,
284         true /* persistent */,
285         Bind([this, &task_ids] {
286           for (int j = 0; j < kNumTasks; ++j) {
287             // Once we cancel all the tasks, none should run, so this code runs
288             // only once from one callback.
289             EXPECT_TRUE(this->loop_->CancelTask(task_ids[j]));
290             task_ids[j] = MessageLoop::kTaskIdNull;
291           }
292         }));
293   }
294   MessageLoopRunMaxIterations(this->loop_.get(), 100);
295   for (int i = 0; i < kNumTasks; ++i) {
296     EXPECT_EQ(MessageLoop::kTaskIdNull, task_ids[i]);
297   }
298 }
299 
300 // Test that if there are several tasks watching for file descriptors to be
301 // available or simply waiting in the message loop are fairly scheduled to run.
302 // In other words, this test ensures that having a file descriptor always
303 // available doesn't prevent other file descriptors watching tasks or delayed
304 // tasks to be dispatched, causing starvation.
TYPED_TEST(MessageLoopTest,AllTasksAreEqual)305 TYPED_TEST(MessageLoopTest, AllTasksAreEqual) {
306   int total_calls = 0;
307 
308   // First, schedule a repeating timeout callback to run from the main loop.
309   int timeout_called = 0;
310   base::Closure timeout_callback;
311   MessageLoop::TaskId timeout_task;
312   timeout_callback = base::Bind(
313       [this, &timeout_called, &total_calls, &timeout_callback, &timeout_task] {
314         timeout_called++;
315         total_calls++;
316         timeout_task = this->loop_->PostTask(FROM_HERE, Bind(timeout_callback));
317         if (total_calls > 100)
318           this->loop_->BreakLoop();
319       });
320   timeout_task = this->loop_->PostTask(FROM_HERE, timeout_callback);
321 
322   // Second, schedule several file descriptor watchers.
323   const int kNumTasks = 3;
324   ScopedPipe pipes[kNumTasks];
325   MessageLoop::TaskId tasks[kNumTasks];
326 
327   int reads[kNumTasks] = {};
328   auto fd_callback = [this, &pipes, &reads, &total_calls](int i) {
329     reads[i]++;
330     total_calls++;
331     char c;
332     EXPECT_EQ(1, HANDLE_EINTR(read(pipes[i].reader, &c, 1)));
333     if (total_calls > 100)
334       this->loop_->BreakLoop();
335   };
336 
337   for (int i = 0; i < kNumTasks; ++i) {
338     tasks[i] = this->loop_->WatchFileDescriptor(
339         FROM_HERE, pipes[i].reader, MessageLoop::kWatchRead,
340         true /* persistent */,
341         Bind(fd_callback, i));
342     // Make enough bytes available on each file descriptor. This should not
343     // block because we set the size of the file descriptor buffer when
344     // creating it.
345     std::vector<char> blob(1000, 'a');
346     EXPECT_EQ(blob.size(),
347               HANDLE_EINTR(write(pipes[i].writer, blob.data(), blob.size())));
348   }
349   this->loop_->Run();
350   EXPECT_GT(total_calls, 100);
351   // We run the loop up 100 times and expect each callback to run at least 10
352   // times. A good scheduler should balance these callbacks.
353   EXPECT_GE(timeout_called, 10);
354   EXPECT_TRUE(this->loop_->CancelTask(timeout_task));
355   for (int i = 0; i < kNumTasks; ++i) {
356     EXPECT_GE(reads[i], 10) << "Reading from pipes[" << i << "], fd "
357                             << pipes[i].reader;
358     EXPECT_TRUE(this->loop_->CancelTask(tasks[i]));
359   }
360 }
361 
362 }  // namespace brillo
363