| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "flutter/testing/test_timeout_listener.h" |
| |
| #include <map> |
| #include <sstream> |
| |
| namespace flutter { |
| namespace testing { |
| |
| class PendingTests : public std::enable_shared_from_this<PendingTests> { |
| public: |
| static std::shared_ptr<PendingTests> Create( |
| fml::RefPtr<fml::TaskRunner> host_task_runner, |
| fml::TimeDelta timeout) { |
| return std::shared_ptr<PendingTests>( |
| new PendingTests(std::move(host_task_runner), timeout)); |
| } |
| |
| ~PendingTests() = default; |
| |
| void OnTestBegin(const std::string& test_name, fml::TimePoint test_time) { |
| FML_CHECK(tests_.find(test_name) == tests_.end()) |
| << "Attempting to start a test that is already pending."; |
| tests_[test_name] = test_time; |
| |
| host_task_runner_->PostDelayedTask( |
| [weak = weak_from_this()] { |
| if (auto strong = weak.lock()) { |
| strong->CheckTimedOutTests(); |
| } |
| }, |
| timeout_); |
| } |
| |
| void OnTestEnd(const std::string& test_name) { tests_.erase(test_name); } |
| |
| void CheckTimedOutTests() const { |
| const auto now = fml::TimePoint::Now(); |
| |
| for (const auto& test : tests_) { |
| auto delay = now - test.second; |
| FML_CHECK(delay < timeout_) |
| << "Test " << test.first << " did not complete in " |
| << timeout_.ToSeconds() |
| << " seconds and is assumed to be hung. Killing the test harness."; |
| } |
| } |
| |
| private: |
| using TestData = std::map<std::string, fml::TimePoint>; |
| |
| fml::RefPtr<fml::TaskRunner> host_task_runner_; |
| TestData tests_; |
| const fml::TimeDelta timeout_; |
| |
| PendingTests(fml::RefPtr<fml::TaskRunner> host_task_runner, |
| fml::TimeDelta timeout) |
| : host_task_runner_(std::move(host_task_runner)), timeout_(timeout) {} |
| |
| FML_DISALLOW_COPY_AND_ASSIGN(PendingTests); |
| }; |
| |
| template <class T> |
| auto WeakPtr(std::shared_ptr<T> pointer) { |
| return std::weak_ptr<T>{pointer}; |
| } |
| |
| TestTimeoutListener::TestTimeoutListener(fml::TimeDelta timeout) |
| : timeout_(timeout), |
| listener_thread_("test_timeout_listener"), |
| listener_thread_runner_(listener_thread_.GetTaskRunner()), |
| pending_tests_(PendingTests::Create(listener_thread_runner_, timeout_)) { |
| FML_LOG(INFO) << "Test timeout of " << timeout_.ToSeconds() |
| << " seconds per test case will be enforced."; |
| } |
| |
| TestTimeoutListener::~TestTimeoutListener() { |
| listener_thread_runner_->PostTask( |
| [tests = std::move(pending_tests_)]() mutable { tests.reset(); }); |
| FML_CHECK(pending_tests_ == nullptr); |
| } |
| |
| static std::string GetTestNameFromTestInfo( |
| const ::testing::TestInfo& test_info) { |
| std::stringstream stream; |
| stream << test_info.test_suite_name(); |
| stream << "."; |
| stream << test_info.name(); |
| if (auto type_param = test_info.type_param()) { |
| stream << "/" << type_param; |
| } |
| if (auto value_param = test_info.value_param()) { |
| stream << "/" << value_param; |
| } |
| return stream.str(); |
| } |
| |
| // |testing::EmptyTestEventListener| |
| void TestTimeoutListener::OnTestStart(const ::testing::TestInfo& test_info) { |
| listener_thread_runner_->PostTask([weak_tests = WeakPtr(pending_tests_), |
| name = GetTestNameFromTestInfo(test_info), |
| now = fml::TimePoint::Now()]() { |
| if (auto tests = weak_tests.lock()) { |
| tests->OnTestBegin(std::move(name), now); |
| } |
| }); |
| } |
| |
| // |testing::EmptyTestEventListener| |
| void TestTimeoutListener::OnTestEnd(const ::testing::TestInfo& test_info) { |
| listener_thread_runner_->PostTask( |
| [weak_tests = WeakPtr(pending_tests_), |
| name = GetTestNameFromTestInfo(test_info)]() { |
| if (auto tests = weak_tests.lock()) { |
| tests->OnTestEnd(std::move(name)); |
| } |
| }); |
| } |
| |
| } // namespace testing |
| } // namespace flutter |