未验证 提交 7ca44d33 编写于 作者: C Chinmay Garde 提交者: GitHub

Kill the test harness if any test exceeds a timeout. (#16349)

Our tests depend on explicit latching to verify assertion are checked. If a test
does not respond for a long time, it has probably encoutered a deadlock. Instead
of waiting for the test runner to detect this, apply a very aggresive timeout on
a per test basis.
上级 677b563b
......@@ -28,6 +28,8 @@ source_set("testing") {
sources = [
"run_all_unittests.cc",
"test_timeout_listener.cc",
"test_timeout_listener.h",
]
public_deps = [
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "flutter/fml/build_config.h"
#include "flutter/testing/test_timeout_listener.h"
#include "gtest/gtest.h"
#ifdef OS_IOS
......@@ -18,5 +19,10 @@ int main(int argc, char** argv) {
#endif // OS_IOS
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
auto timeout_listener = new flutter::testing::TestTimeoutListener();
auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
listeners.Append(timeout_listener);
auto result = RUN_ALL_TESTS();
delete listeners.Release(timeout_listener);
return result;
}
// 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_)) {}
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
// 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.
#ifndef FLUTTER_TESTING_TEST_TIMEOUT_LISTENER_H_
#define FLUTTER_TESTING_TEST_TIMEOUT_LISTENER_H_
#include <memory>
#include "flutter/fml/macros.h"
#include "flutter/fml/task_runner.h"
#include "flutter/fml/thread.h"
#include "flutter/testing/testing.h"
namespace flutter {
namespace testing {
class PendingTests;
class TestTimeoutListener : public ::testing::EmptyTestEventListener {
public:
TestTimeoutListener(
fml::TimeDelta timeout = fml::TimeDelta::FromSeconds(30u));
~TestTimeoutListener();
private:
const fml::TimeDelta timeout_;
fml::Thread listener_thread_;
fml::RefPtr<fml::TaskRunner> listener_thread_runner_;
std::shared_ptr<PendingTests> pending_tests_;
// |testing::EmptyTestEventListener|
void OnTestStart(const ::testing::TestInfo& test_info) override;
// |testing::EmptyTestEventListener|
void OnTestEnd(const ::testing::TestInfo& test_info) override;
FML_DISALLOW_COPY_AND_ASSIGN(TestTimeoutListener);
};
} // namespace testing
} // namespace flutter
#endif // FLUTTER_TESTING_TEST_TIMEOUT_LISTENER_H_
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册