1 | // Copyright 2013 The Flutter 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 "flutter/testing/test_timeout_listener.h" |
6 | |
7 | #include <map> |
8 | #include <sstream> |
9 | |
10 | namespace flutter { |
11 | namespace testing { |
12 | |
13 | class PendingTests : public std::enable_shared_from_this<PendingTests> { |
14 | public: |
15 | static std::shared_ptr<PendingTests> Create( |
16 | fml::RefPtr<fml::TaskRunner> host_task_runner, |
17 | fml::TimeDelta timeout) { |
18 | return std::shared_ptr<PendingTests>( |
19 | new PendingTests(std::move(host_task_runner), timeout)); |
20 | } |
21 | |
22 | ~PendingTests() = default; |
23 | |
24 | void OnTestBegin(const std::string& test_name, fml::TimePoint test_time) { |
25 | FML_CHECK(tests_.find(test_name) == tests_.end()) |
26 | << "Attempting to start a test that is already pending." ; |
27 | tests_[test_name] = test_time; |
28 | |
29 | host_task_runner_->PostDelayedTask( |
30 | [weak = weak_from_this()] { |
31 | if (auto strong = weak.lock()) { |
32 | strong->CheckTimedOutTests(); |
33 | } |
34 | }, |
35 | timeout_); |
36 | } |
37 | |
38 | void OnTestEnd(const std::string& test_name) { tests_.erase(test_name); } |
39 | |
40 | void CheckTimedOutTests() const { |
41 | const auto now = fml::TimePoint::Now(); |
42 | |
43 | for (const auto& test : tests_) { |
44 | auto delay = now - test.second; |
45 | FML_CHECK(delay < timeout_) |
46 | << "Test " << test.first << " did not complete in " |
47 | << timeout_.ToSeconds() |
48 | << " seconds and is assumed to be hung. Killing the test harness." ; |
49 | } |
50 | } |
51 | |
52 | private: |
53 | using TestData = std::map<std::string, fml::TimePoint>; |
54 | |
55 | fml::RefPtr<fml::TaskRunner> host_task_runner_; |
56 | TestData tests_; |
57 | const fml::TimeDelta timeout_; |
58 | |
59 | PendingTests(fml::RefPtr<fml::TaskRunner> host_task_runner, |
60 | fml::TimeDelta timeout) |
61 | : host_task_runner_(std::move(host_task_runner)), timeout_(timeout) {} |
62 | |
63 | FML_DISALLOW_COPY_AND_ASSIGN(PendingTests); |
64 | }; |
65 | |
66 | template <class T> |
67 | auto WeakPtr(std::shared_ptr<T> pointer) { |
68 | return std::weak_ptr<T>{pointer}; |
69 | } |
70 | |
71 | TestTimeoutListener::TestTimeoutListener(fml::TimeDelta timeout) |
72 | : timeout_(timeout), |
73 | listener_thread_("test_timeout_listener" ), |
74 | listener_thread_runner_(listener_thread_.GetTaskRunner()), |
75 | pending_tests_(PendingTests::Create(listener_thread_runner_, timeout_)) { |
76 | FML_LOG(INFO) << "Test timeout of " << timeout_.ToSeconds() |
77 | << " seconds per test case will be enforced." ; |
78 | } |
79 | |
80 | TestTimeoutListener::~TestTimeoutListener() { |
81 | listener_thread_runner_->PostTask( |
82 | [tests = std::move(pending_tests_)]() mutable { tests.reset(); }); |
83 | FML_CHECK(pending_tests_ == nullptr); |
84 | } |
85 | |
86 | static std::string GetTestNameFromTestInfo( |
87 | const ::testing::TestInfo& test_info) { |
88 | std::stringstream stream; |
89 | stream << test_info.test_suite_name(); |
90 | stream << "." ; |
91 | stream << test_info.name(); |
92 | if (auto type_param = test_info.type_param()) { |
93 | stream << "/" << type_param; |
94 | } |
95 | if (auto value_param = test_info.value_param()) { |
96 | stream << "/" << value_param; |
97 | } |
98 | return stream.str(); |
99 | } |
100 | |
101 | // |testing::EmptyTestEventListener| |
102 | void TestTimeoutListener::OnTestStart(const ::testing::TestInfo& test_info) { |
103 | listener_thread_runner_->PostTask([weak_tests = WeakPtr(pending_tests_), |
104 | name = GetTestNameFromTestInfo(test_info), |
105 | now = fml::TimePoint::Now()]() { |
106 | if (auto tests = weak_tests.lock()) { |
107 | tests->OnTestBegin(std::move(name), now); |
108 | } |
109 | }); |
110 | } |
111 | |
112 | // |testing::EmptyTestEventListener| |
113 | void TestTimeoutListener::OnTestEnd(const ::testing::TestInfo& test_info) { |
114 | listener_thread_runner_->PostTask( |
115 | [weak_tests = WeakPtr(pending_tests_), |
116 | name = GetTestNameFromTestInfo(test_info)]() { |
117 | if (auto tests = weak_tests.lock()) { |
118 | tests->OnTestEnd(std::move(name)); |
119 | } |
120 | }); |
121 | } |
122 | |
123 | } // namespace testing |
124 | } // namespace flutter |
125 | |