blob: e4604cee0fa846d2a746003a1760f4d422d5f562 [file] [log] [blame]
// 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