1 // Copyright 2019 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/logging.h"
11 #include "base/strings/strcat.h"
12 #include "base/time/time.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace base {
16 namespace test {
17
18 namespace {
19
20 bool g_add_gtest_failure_on_timeout = false;
21
TimeoutMessage(const RepeatingCallback<std::string ()> & get_log,const Location & timeout_enabled_from_here)22 std::string TimeoutMessage(const RepeatingCallback<std::string()>& get_log,
23 const Location& timeout_enabled_from_here) {
24 std::string message = "RunLoop::Run() timed out. Timeout set at ";
25 message += timeout_enabled_from_here.ToString() + ".";
26 if (get_log)
27 StrAppend(&message, {"\n", get_log.Run()});
28 return message;
29 }
30
31 } // namespace
32
ScopedRunLoopTimeout(const Location & from_here,TimeDelta timeout)33 ScopedRunLoopTimeout::ScopedRunLoopTimeout(const Location& from_here,
34 TimeDelta timeout)
35 : ScopedRunLoopTimeout(from_here, timeout, NullCallback()) {}
36
~ScopedRunLoopTimeout()37 ScopedRunLoopTimeout::~ScopedRunLoopTimeout() {
38 RunLoop::SetTimeoutForCurrentThread(nested_timeout_);
39 }
40
ScopedRunLoopTimeout(const Location & timeout_enabled_from_here,TimeDelta timeout,RepeatingCallback<std::string ()> on_timeout_log)41 ScopedRunLoopTimeout::ScopedRunLoopTimeout(
42 const Location& timeout_enabled_from_here,
43 TimeDelta timeout,
44 RepeatingCallback<std::string()> on_timeout_log)
45 : nested_timeout_(RunLoop::GetTimeoutForCurrentThread()) {
46 DCHECK_GT(timeout, TimeDelta());
47 run_timeout_.timeout = timeout;
48
49 if (g_add_gtest_failure_on_timeout) {
50 run_timeout_.on_timeout = BindRepeating(
51 [](const Location& timeout_enabled_from_here,
52 RepeatingCallback<std::string()> on_timeout_log,
53 const Location& run_from_here) {
54 GTEST_FAIL_AT(run_from_here.file_name(), run_from_here.line_number())
55 << TimeoutMessage(on_timeout_log, timeout_enabled_from_here);
56 },
57 timeout_enabled_from_here, std::move(on_timeout_log));
58 } else {
59 run_timeout_.on_timeout = BindRepeating(
60 [](const Location& timeout_enabled_from_here,
61 RepeatingCallback<std::string()> on_timeout_log,
62 const Location& run_from_here) {
63 std::string message =
64 TimeoutMessage(on_timeout_log, timeout_enabled_from_here);
65 logging::LogMessage(run_from_here.file_name(),
66 run_from_here.line_number(), message.data());
67 },
68 timeout_enabled_from_here, std::move(on_timeout_log));
69 }
70
71 RunLoop::SetTimeoutForCurrentThread(&run_timeout_);
72 }
73
74 // static
ExistsForCurrentThread()75 bool ScopedRunLoopTimeout::ExistsForCurrentThread() {
76 return RunLoop::GetTimeoutForCurrentThread() != nullptr;
77 }
78
79 // static
SetAddGTestFailureOnTimeout()80 void ScopedRunLoopTimeout::SetAddGTestFailureOnTimeout() {
81 g_add_gtest_failure_on_timeout = true;
82 }
83
84 // static
85 const RunLoop::RunLoopTimeout*
GetTimeoutForCurrentThread()86 ScopedRunLoopTimeout::GetTimeoutForCurrentThread() {
87 return RunLoop::GetTimeoutForCurrentThread();
88 }
89
ScopedDisableRunLoopTimeout()90 ScopedDisableRunLoopTimeout::ScopedDisableRunLoopTimeout()
91 : nested_timeout_(RunLoop::GetTimeoutForCurrentThread()) {
92 RunLoop::SetTimeoutForCurrentThread(nullptr);
93 }
94
~ScopedDisableRunLoopTimeout()95 ScopedDisableRunLoopTimeout::~ScopedDisableRunLoopTimeout() {
96 RunLoop::SetTimeoutForCurrentThread(nested_timeout_);
97 }
98
99 } // namespace test
100 } // namespace base
101