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