• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 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 "base/test/scoped_task_environment.h"
6 
7 #include <memory>
8 
9 #include "base/atomicops.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/synchronization/atomic_flag.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/task_scheduler/post_task.h"
15 #include "base/test/test_timeouts.h"
16 #include "base/threading/platform_thread.h"
17 #include "base/threading/sequence_local_storage_slot.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "base/time/tick_clock.h"
20 #include "build/build_config.h"
21 #include "testing/gtest/include/gtest/gtest.h"
22 
23 #if defined(OS_POSIX)
24 #include <unistd.h>
25 #include "base/files/file_descriptor_watcher_posix.h"
26 #endif  // defined(OS_POSIX)
27 
28 namespace base {
29 namespace test {
30 
31 namespace {
32 
33 class ScopedTaskEnvironmentTest
34     : public testing::TestWithParam<ScopedTaskEnvironment::MainThreadType> {};
35 
VerifyRunUntilIdleDidNotReturnAndSetFlag(AtomicFlag * run_until_idle_returned,AtomicFlag * task_ran)36 void VerifyRunUntilIdleDidNotReturnAndSetFlag(
37     AtomicFlag* run_until_idle_returned,
38     AtomicFlag* task_ran) {
39   EXPECT_FALSE(run_until_idle_returned->IsSet());
40   task_ran->Set();
41 }
42 
RunUntilIdleTest(ScopedTaskEnvironment::MainThreadType main_thread_type,ScopedTaskEnvironment::ExecutionMode execution_control_mode)43 void RunUntilIdleTest(
44     ScopedTaskEnvironment::MainThreadType main_thread_type,
45     ScopedTaskEnvironment::ExecutionMode execution_control_mode) {
46   AtomicFlag run_until_idle_returned;
47   ScopedTaskEnvironment scoped_task_environment(main_thread_type,
48                                                 execution_control_mode);
49 
50   AtomicFlag first_main_thread_task_ran;
51   ThreadTaskRunnerHandle::Get()->PostTask(
52       FROM_HERE, BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
53                           Unretained(&run_until_idle_returned),
54                           Unretained(&first_main_thread_task_ran)));
55 
56   AtomicFlag first_task_scheduler_task_ran;
57   PostTask(FROM_HERE, BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
58                                Unretained(&run_until_idle_returned),
59                                Unretained(&first_task_scheduler_task_ran)));
60 
61   AtomicFlag second_task_scheduler_task_ran;
62   AtomicFlag second_main_thread_task_ran;
63   PostTaskAndReply(FROM_HERE,
64                    BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
65                             Unretained(&run_until_idle_returned),
66                             Unretained(&second_task_scheduler_task_ran)),
67                    BindOnce(&VerifyRunUntilIdleDidNotReturnAndSetFlag,
68                             Unretained(&run_until_idle_returned),
69                             Unretained(&second_main_thread_task_ran)));
70 
71   scoped_task_environment.RunUntilIdle();
72   run_until_idle_returned.Set();
73 
74   EXPECT_TRUE(first_main_thread_task_ran.IsSet());
75   EXPECT_TRUE(first_task_scheduler_task_ran.IsSet());
76   EXPECT_TRUE(second_task_scheduler_task_ran.IsSet());
77   EXPECT_TRUE(second_main_thread_task_ran.IsSet());
78 }
79 
80 }  // namespace
81 
TEST_P(ScopedTaskEnvironmentTest,QueuedRunUntilIdle)82 TEST_P(ScopedTaskEnvironmentTest, QueuedRunUntilIdle) {
83   RunUntilIdleTest(GetParam(), ScopedTaskEnvironment::ExecutionMode::QUEUED);
84 }
85 
TEST_P(ScopedTaskEnvironmentTest,AsyncRunUntilIdle)86 TEST_P(ScopedTaskEnvironmentTest, AsyncRunUntilIdle) {
87   RunUntilIdleTest(GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
88 }
89 
90 // Verify that tasks posted to an ExecutionMode::QUEUED ScopedTaskEnvironment do
91 // not run outside of RunUntilIdle().
TEST_P(ScopedTaskEnvironmentTest,QueuedTasksDoNotRunOutsideOfRunUntilIdle)92 TEST_P(ScopedTaskEnvironmentTest, QueuedTasksDoNotRunOutsideOfRunUntilIdle) {
93   ScopedTaskEnvironment scoped_task_environment(
94       GetParam(), ScopedTaskEnvironment::ExecutionMode::QUEUED);
95 
96   AtomicFlag run_until_idle_called;
97   PostTask(FROM_HERE, BindOnce(
98                           [](AtomicFlag* run_until_idle_called) {
99                             EXPECT_TRUE(run_until_idle_called->IsSet());
100                           },
101                           Unretained(&run_until_idle_called)));
102   PlatformThread::Sleep(TestTimeouts::tiny_timeout());
103   run_until_idle_called.Set();
104   scoped_task_environment.RunUntilIdle();
105 
106   AtomicFlag other_run_until_idle_called;
107   PostTask(FROM_HERE, BindOnce(
108                           [](AtomicFlag* other_run_until_idle_called) {
109                             EXPECT_TRUE(other_run_until_idle_called->IsSet());
110                           },
111                           Unretained(&other_run_until_idle_called)));
112   PlatformThread::Sleep(TestTimeouts::tiny_timeout());
113   other_run_until_idle_called.Set();
114   scoped_task_environment.RunUntilIdle();
115 }
116 
117 // Verify that a task posted to an ExecutionMode::ASYNC ScopedTaskEnvironment
118 // can run without a call to RunUntilIdle().
TEST_P(ScopedTaskEnvironmentTest,AsyncTasksRunAsTheyArePosted)119 TEST_P(ScopedTaskEnvironmentTest, AsyncTasksRunAsTheyArePosted) {
120   ScopedTaskEnvironment scoped_task_environment(
121       GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
122 
123   WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
124                          WaitableEvent::InitialState::NOT_SIGNALED);
125   PostTask(FROM_HERE,
126            BindOnce([](WaitableEvent* task_ran) { task_ran->Signal(); },
127                     Unretained(&task_ran)));
128   task_ran.Wait();
129 }
130 
131 // Verify that a task posted to an ExecutionMode::ASYNC ScopedTaskEnvironment
132 // after a call to RunUntilIdle() can run without another call to
133 // RunUntilIdle().
TEST_P(ScopedTaskEnvironmentTest,AsyncTasksRunAsTheyArePostedAfterRunUntilIdle)134 TEST_P(ScopedTaskEnvironmentTest,
135        AsyncTasksRunAsTheyArePostedAfterRunUntilIdle) {
136   ScopedTaskEnvironment scoped_task_environment(
137       GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
138 
139   scoped_task_environment.RunUntilIdle();
140 
141   WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL,
142                          WaitableEvent::InitialState::NOT_SIGNALED);
143   PostTask(FROM_HERE,
144            BindOnce([](WaitableEvent* task_ran) { task_ran->Signal(); },
145                     Unretained(&task_ran)));
146   task_ran.Wait();
147 }
148 
TEST_P(ScopedTaskEnvironmentTest,DelayedTasks)149 TEST_P(ScopedTaskEnvironmentTest, DelayedTasks) {
150   // Use a QUEUED execution-mode environment, so that no tasks are actually
151   // executed until RunUntilIdle()/FastForwardBy() are invoked.
152   ScopedTaskEnvironment scoped_task_environment(
153       GetParam(), ScopedTaskEnvironment::ExecutionMode::QUEUED);
154 
155   subtle::Atomic32 counter = 0;
156 
157   constexpr base::TimeDelta kShortTaskDelay = TimeDelta::FromDays(1);
158   // Should run only in MOCK_TIME environment when time is fast-forwarded.
159   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
160       FROM_HERE,
161       Bind(
162           [](subtle::Atomic32* counter) {
163             subtle::NoBarrier_AtomicIncrement(counter, 4);
164           },
165           Unretained(&counter)),
166       kShortTaskDelay);
167   // TODO(gab): This currently doesn't run because the TaskScheduler's clock
168   // isn't mocked but it should be.
169   PostDelayedTask(FROM_HERE,
170                   Bind(
171                       [](subtle::Atomic32* counter) {
172                         subtle::NoBarrier_AtomicIncrement(counter, 128);
173                       },
174                       Unretained(&counter)),
175                   kShortTaskDelay);
176 
177   constexpr base::TimeDelta kLongTaskDelay = TimeDelta::FromDays(7);
178   // Same as first task, longer delays to exercise
179   // FastForwardUntilNoTasksRemain().
180   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
181       FROM_HERE,
182       Bind(
183           [](subtle::Atomic32* counter) {
184             subtle::NoBarrier_AtomicIncrement(counter, 8);
185           },
186           Unretained(&counter)),
187       TimeDelta::FromDays(5));
188   ThreadTaskRunnerHandle::Get()->PostDelayedTask(
189       FROM_HERE,
190       Bind(
191           [](subtle::Atomic32* counter) {
192             subtle::NoBarrier_AtomicIncrement(counter, 16);
193           },
194           Unretained(&counter)),
195       kLongTaskDelay);
196 
197   ThreadTaskRunnerHandle::Get()->PostTask(
198       FROM_HERE, Bind(
199                      [](subtle::Atomic32* counter) {
200                        subtle::NoBarrier_AtomicIncrement(counter, 1);
201                      },
202                      Unretained(&counter)));
203   PostTask(FROM_HERE, Bind(
204                           [](subtle::Atomic32* counter) {
205                             subtle::NoBarrier_AtomicIncrement(counter, 2);
206                           },
207                           Unretained(&counter)));
208 
209   // This expectation will fail flakily if the preceding PostTask() is executed
210   // asynchronously, indicating a problem with the QUEUED execution mode.
211   int expected_value = 0;
212   EXPECT_EQ(expected_value, counter);
213 
214   // RunUntilIdle() should process non-delayed tasks only in all queues.
215   scoped_task_environment.RunUntilIdle();
216   expected_value += 1;
217   expected_value += 2;
218   EXPECT_EQ(expected_value, counter);
219 
220   if (GetParam() == ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {
221     // Delay inferior to the delay of the first posted task.
222     constexpr base::TimeDelta kInferiorTaskDelay = TimeDelta::FromSeconds(1);
223     static_assert(kInferiorTaskDelay < kShortTaskDelay,
224                   "|kInferiorTaskDelay| should be "
225                   "set to a value inferior to the first posted task's delay.");
226     scoped_task_environment.FastForwardBy(kInferiorTaskDelay);
227     EXPECT_EQ(expected_value, counter);
228 
229     scoped_task_environment.FastForwardBy(kShortTaskDelay - kInferiorTaskDelay);
230     expected_value += 4;
231     EXPECT_EQ(expected_value, counter);
232 
233     scoped_task_environment.FastForwardUntilNoTasksRemain();
234     expected_value += 8;
235     expected_value += 16;
236     EXPECT_EQ(expected_value, counter);
237   }
238 }
239 
240 // Regression test for https://crbug.com/824770.
TEST_P(ScopedTaskEnvironmentTest,SupportsSequenceLocalStorageOnMainThread)241 TEST_P(ScopedTaskEnvironmentTest, SupportsSequenceLocalStorageOnMainThread) {
242   ScopedTaskEnvironment scoped_task_environment(
243       GetParam(), ScopedTaskEnvironment::ExecutionMode::ASYNC);
244 
245   SequenceLocalStorageSlot<int> sls_slot;
246   sls_slot.Set(5);
247   EXPECT_EQ(5, sls_slot.Get());
248 }
249 
250 #if defined(OS_POSIX)
TEST_F(ScopedTaskEnvironmentTest,SupportsFileDescriptorWatcherOnIOMainThread)251 TEST_F(ScopedTaskEnvironmentTest, SupportsFileDescriptorWatcherOnIOMainThread) {
252   ScopedTaskEnvironment scoped_task_environment(
253       ScopedTaskEnvironment::MainThreadType::IO,
254       ScopedTaskEnvironment::ExecutionMode::ASYNC);
255 
256   int pipe_fds_[2];
257   ASSERT_EQ(0, pipe(pipe_fds_));
258 
259   RunLoop run_loop;
260 
261   // The write end of a newly created pipe is immediately writable.
262   auto controller = FileDescriptorWatcher::WatchWritable(
263       pipe_fds_[1], run_loop.QuitClosure());
264 
265   // This will hang if the notification doesn't occur as expected.
266   run_loop.Run();
267 }
268 #endif  // defined(OS_POSIX)
269 
270 // Verify that the TickClock returned by
271 // |ScopedTaskEnvironment::GetMockTickClock| gets updated when the
272 // FastForward(By|UntilNoTasksRemain) functions are called.
TEST_F(ScopedTaskEnvironmentTest,FastForwardAdvanceTickClock)273 TEST_F(ScopedTaskEnvironmentTest, FastForwardAdvanceTickClock) {
274   // Use a QUEUED execution-mode environment, so that no tasks are actually
275   // executed until RunUntilIdle()/FastForwardBy() are invoked.
276   ScopedTaskEnvironment scoped_task_environment(
277       ScopedTaskEnvironment::MainThreadType::MOCK_TIME,
278       ScopedTaskEnvironment::ExecutionMode::QUEUED);
279 
280   constexpr base::TimeDelta kShortTaskDelay = TimeDelta::FromDays(1);
281   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
282                                                  kShortTaskDelay);
283 
284   constexpr base::TimeDelta kLongTaskDelay = TimeDelta::FromDays(7);
285   ThreadTaskRunnerHandle::Get()->PostDelayedTask(FROM_HERE, base::DoNothing(),
286                                                  kLongTaskDelay);
287 
288   const base::TickClock* tick_clock =
289       scoped_task_environment.GetMockTickClock();
290   base::TimeTicks tick_clock_ref = tick_clock->NowTicks();
291 
292   // Make sure that |FastForwardBy| advances the clock.
293   scoped_task_environment.FastForwardBy(kShortTaskDelay);
294   EXPECT_EQ(kShortTaskDelay, tick_clock->NowTicks() - tick_clock_ref);
295 
296   // Make sure that |FastForwardUntilNoTasksRemain| advances the clock.
297   scoped_task_environment.FastForwardUntilNoTasksRemain();
298   EXPECT_EQ(kLongTaskDelay, tick_clock->NowTicks() - tick_clock_ref);
299 
300   // Fast-forwarding to a time at which there's no tasks should also advance the
301   // clock.
302   scoped_task_environment.FastForwardBy(kLongTaskDelay);
303   EXPECT_EQ(kLongTaskDelay * 2, tick_clock->NowTicks() - tick_clock_ref);
304 }
305 
306 INSTANTIATE_TEST_CASE_P(
307     MainThreadDefault,
308     ScopedTaskEnvironmentTest,
309     ::testing::Values(ScopedTaskEnvironment::MainThreadType::DEFAULT));
310 INSTANTIATE_TEST_CASE_P(
311     MainThreadMockTime,
312     ScopedTaskEnvironmentTest,
313     ::testing::Values(ScopedTaskEnvironment::MainThreadType::MOCK_TIME));
314 INSTANTIATE_TEST_CASE_P(
315     MainThreadUI,
316     ScopedTaskEnvironmentTest,
317     ::testing::Values(ScopedTaskEnvironment::MainThreadType::UI));
318 INSTANTIATE_TEST_CASE_P(
319     MainThreadIO,
320     ScopedTaskEnvironmentTest,
321     ::testing::Values(ScopedTaskEnvironment::MainThreadType::IO));
322 
323 }  // namespace test
324 }  // namespace base
325