Home | History | Annotate | Download | only in message_loops
      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:
     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 <>
     52 void MessageLoopTest<GlibMessageLoop>::MessageLoopSetUp() {
     53   loop_.reset(new GlibMessageLoop());
     54 }
     55 
     56 template <>
     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 
     70 TYPED_TEST(MessageLoopTest, CancelTaskInvalidValuesTest) {
     71   EXPECT_FALSE(this->loop_->CancelTask(MessageLoop::kTaskIdNull));
     72   EXPECT_FALSE(this->loop_->CancelTask(1234));
     73 }
     74 
     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.
     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 
     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.
    114 TYPED_TEST(MessageLoopTest, PostDelayedTaskWithoutLocation) {
    115   this->loop_->PostDelayedTask(Bind(&base::DoNothing), TimeDelta());
    116   EXPECT_EQ(1, MessageLoopRunMaxIterations(this->loop_.get(), 100));
    117 }
    118 
    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 
    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.
    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.
    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 
    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 
    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.
    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.
    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.
    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.
    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.
    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