1 // Copyright 2020 The Chromium Authors
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_run_loop_timeout.h"
6
7 #include "base/functional/bind.h"
8 #include "base/functional/callback_helpers.h"
9 #include "base/location.h"
10 #include "base/task/sequenced_task_runner.h"
11 #include "base/test/bind.h"
12 #include "base/test/gtest_util.h"
13 #include "base/test/mock_callback.h"
14 #include "base/test/task_environment.h"
15 #include "base/time/time.h"
16 #include "testing/gtest/include/gtest/gtest-spi.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18
19 namespace base::test {
20
TEST(ScopedRunLoopTimeoutTest,TimesOut)21 TEST(ScopedRunLoopTimeoutTest, TimesOut) {
22 TaskEnvironment task_environment;
23 RunLoop run_loop;
24
25 static constexpr auto kArbitraryTimeout = Milliseconds(10);
26 ScopedRunLoopTimeout run_timeout(FROM_HERE, kArbitraryTimeout);
27
28 // Since the delayed task will be posted only after the message pump starts
29 // running, the ScopedRunLoopTimeout will already have started to elapse,
30 // so if Run() exits at the correct time then our delayed task will not run.
31 SequencedTaskRunner::GetCurrentDefault()->PostTask(
32 FROM_HERE,
33 BindOnce(IgnoreResult(&SequencedTaskRunner::PostDelayedTask),
34 SequencedTaskRunner::GetCurrentDefault(), FROM_HERE,
35 MakeExpectedNotRunClosure(FROM_HERE), kArbitraryTimeout));
36
37 // This task should get to run before Run() times-out.
38 SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
39 FROM_HERE, MakeExpectedRunClosure(FROM_HERE), kArbitraryTimeout);
40
41 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
42 static RunLoop& static_loop = run_loop;
43 EXPECT_NONFATAL_FAILURE(static_loop.Run(), "Run() timed out.");
44 }
45
TEST(ScopedRunLoopTimeoutTest,RunTasksUntilTimeout)46 TEST(ScopedRunLoopTimeoutTest, RunTasksUntilTimeout) {
47 TaskEnvironment task_environment;
48 RunLoop run_loop;
49
50 static constexpr auto kArbitraryTimeout = Milliseconds(10);
51 ScopedRunLoopTimeout run_timeout(FROM_HERE, kArbitraryTimeout);
52
53 // Posting a task with the same delay as our timeout, immediately before
54 // calling Run(), means it should get to run. Since this uses QuitWhenIdle(),
55 // the Run() timeout callback should also get to run.
56 SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
57 FROM_HERE, MakeExpectedRunClosure(FROM_HERE), kArbitraryTimeout);
58
59 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
60 static RunLoop& static_loop = run_loop;
61 EXPECT_NONFATAL_FAILURE(static_loop.Run(), "Run() timed out.");
62 }
63
TEST(ScopedRunLoopTimeoutTest,TimesOutWithInheritedTimeoutValue)64 TEST(ScopedRunLoopTimeoutTest, TimesOutWithInheritedTimeoutValue) {
65 testing::StrictMock<base::MockCallback<RepeatingCallback<std::string()>>>
66 log_callback;
67 TaskEnvironment task_environment;
68 RunLoop run_loop;
69
70 static constexpr auto kArbitraryTimeout = Milliseconds(10);
71 ScopedRunLoopTimeout run_timeout(FROM_HERE, kArbitraryTimeout);
72 ScopedRunLoopTimeout run_timeout2(FROM_HERE, std::nullopt,
73 log_callback.Get());
74
75 // Since the delayed task will be posted only after the message pump starts
76 // running, the ScopedRunLoopTimeout will already have started to elapse,
77 // so if Run() exits at the correct time then our delayed task will not run.
78 SequencedTaskRunner::GetCurrentDefault()->PostTask(
79 FROM_HERE,
80 BindOnce(IgnoreResult(&SequencedTaskRunner::PostDelayedTask),
81 SequencedTaskRunner::GetCurrentDefault(), FROM_HERE,
82 MakeExpectedNotRunClosure(FROM_HERE), kArbitraryTimeout));
83
84 // This task should get to run before Run() times-out.
85 SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
86 FROM_HERE, MakeExpectedRunClosure(FROM_HERE), kArbitraryTimeout);
87
88 EXPECT_CALL(log_callback, Run).WillOnce(testing::Return(std::string()));
89
90 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
91 static RunLoop& static_loop = run_loop;
92 EXPECT_NONFATAL_FAILURE(static_loop.Run(), "Run() timed out.");
93 }
94
TEST(ScopedRunLoopTimeoutTest,RunTasksUntilTimeoutWithInheritedTimeoutValue)95 TEST(ScopedRunLoopTimeoutTest, RunTasksUntilTimeoutWithInheritedTimeoutValue) {
96 testing::StrictMock<base::MockCallback<RepeatingCallback<std::string()>>>
97 log_callback;
98 TaskEnvironment task_environment;
99 RunLoop run_loop;
100
101 static constexpr auto kArbitraryTimeout = Milliseconds(10);
102 ScopedRunLoopTimeout run_timeout(FROM_HERE, kArbitraryTimeout);
103 ScopedRunLoopTimeout run_timeout2(FROM_HERE, std::nullopt,
104 log_callback.Get());
105
106 // Posting a task with the same delay as our timeout, immediately before
107 // calling Run(), means it should get to run. Since this uses QuitWhenIdle(),
108 // the Run() timeout callback should also get to run.
109 SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
110 FROM_HERE, MakeExpectedRunClosure(FROM_HERE), kArbitraryTimeout);
111
112 EXPECT_CALL(log_callback, Run).WillOnce(testing::Return(std::string()));
113
114 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
115 static RunLoop& static_loop = run_loop;
116 EXPECT_NONFATAL_FAILURE(static_loop.Run(), "Run() timed out.");
117 }
118
119 namespace {
120
121 constexpr char kErrorMessage[] = "I like kittens!";
122
123 // Previously these tests hard-coded the file and line numbers; this function
124 // instead generates the expected message given any `FROM_HERE` type location.
GetExpectedTimeoutMessage(const Location & from,const char * expected_message)125 std::string GetExpectedTimeoutMessage(const Location& from,
126 const char* expected_message) {
127 std::ostringstream oss;
128 oss << "RunLoop::Run() timed out. Timeout set at " << from.function_name()
129 << "@" << from.file_name() << ":" << from.line_number() << ".\n"
130 << expected_message;
131 return oss.str();
132 }
133
134 } // namespace
135
TEST(ScopedRunLoopTimeoutTest,OnTimeoutLog)136 TEST(ScopedRunLoopTimeoutTest, OnTimeoutLog) {
137 TaskEnvironment task_environment;
138 RunLoop run_loop;
139
140 static constexpr auto kArbitraryTimeout = Milliseconds(10);
141 const auto location = FROM_HERE;
142 ScopedRunLoopTimeout run_timeout(
143 location, kArbitraryTimeout,
144 BindRepeating([]() -> std::string { return kErrorMessage; }));
145
146 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
147 static RunLoop& static_loop = run_loop;
148 EXPECT_NONFATAL_FAILURE(static_loop.Run(),
149 GetExpectedTimeoutMessage(location, kErrorMessage));
150 }
151
TEST(ScopedRunLoopTimeoutTest,OnTimeoutLogWithNestedTimeouts)152 TEST(ScopedRunLoopTimeoutTest, OnTimeoutLogWithNestedTimeouts) {
153 TaskEnvironment task_environment;
154 RunLoop run_loop;
155
156 static constexpr auto kArbitraryTimeout = Milliseconds(10);
157 ScopedRunLoopTimeout run_timeout(
158 FROM_HERE, base::Hours(1),
159 BindRepeating([]() -> std::string { return "I like puppies!"; }));
160 const auto location = FROM_HERE;
161 ScopedRunLoopTimeout run_timeout2(
162 location, kArbitraryTimeout,
163 BindRepeating([]() -> std::string { return kErrorMessage; }));
164
165 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
166 static RunLoop& static_loop = run_loop;
167 EXPECT_NONFATAL_FAILURE(static_loop.Run(),
168 GetExpectedTimeoutMessage(location, kErrorMessage));
169 }
170
TEST(ScopedRunLoopTimeoutTest,OverwriteTimeoutCallbackForTesting)171 TEST(ScopedRunLoopTimeoutTest, OverwriteTimeoutCallbackForTesting) {
172 TaskEnvironment task_environment;
173 RunLoop run_loop;
174
175 bool custom_handler_called = false;
176 ScopedRunLoopTimeout::TimeoutCallback cb = DoNothing();
177 ScopedRunLoopTimeout::SetTimeoutCallbackForTesting(
178 std::make_unique<ScopedRunLoopTimeout::TimeoutCallback>(
179 std::move(cb).Then(BindLambdaForTesting(
180 [&custom_handler_called]() { custom_handler_called = true; }))));
181 static constexpr auto kArbitraryTimeout = Milliseconds(1);
182 const auto location = FROM_HERE;
183 ScopedRunLoopTimeout run_timeout(
184 location, kArbitraryTimeout,
185 BindRepeating([]() -> std::string { return kErrorMessage; }));
186
187 // EXPECT_NONFATAL_FAILURE() can only reference globals and statics.
188 static RunLoop& static_loop = run_loop;
189 EXPECT_NONFATAL_FAILURE(static_loop.Run(),
190 GetExpectedTimeoutMessage(location, kErrorMessage));
191
192 EXPECT_TRUE(custom_handler_called);
193
194 ScopedRunLoopTimeout::SetTimeoutCallbackForTesting(nullptr);
195 }
196
197 } // namespace base::test
198