0

Report which RunLoop is timing out in a test.

Tests insert a timeout on RunLoop in order to find slow or flaky tests
but the current error message only reports where the timeout is enabled
and not which RunLoop was currently running. Example:

../../content/public/test/browser_test_base.cc:701: Failure
Failed
RunLoop::Run() timed out.
Stack trace:

We will now include the Location where the RunLoop::Run() call happens
in order to report where the test is actually timing out. The new error
looks like:

../../content/test/content_browser_test_test.cc:341: Failure
Failed
RunLoop::Run() timed out. Timeout set at ../../content/public/test/browser_test_base.cc:701.
Stack trace:

Notably the file and line number where the "Failure" is reported
(in the first line) are where the RunLoop is Run().

R=gab@chromium.org

Bug: 1145280
Change-Id: Ia5e39dbbfb47fd4c9f831cb77889061c4cd3060f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518125
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Bill Budge <bbudge@chromium.org>
Reviewed-by: Gabriel Charette <gab@chromium.org>
Commit-Queue: danakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/master@{#845815}
This commit is contained in:
danakj
2021-01-21 22:06:31 +00:00
committed by Chromium LUCI CQ
parent 7bde42510d
commit e125e8d666
9 changed files with 55 additions and 33 deletions

@ -19,7 +19,7 @@ namespace base {
namespace {
ThreadLocalPointer<RunLoop::Delegate>& GetTlsDelegate() {
static base::NoDestructor<ThreadLocalPointer<RunLoop::Delegate>> instance;
static NoDestructor<ThreadLocalPointer<RunLoop::Delegate>> instance;
return *instance;
}
@ -39,9 +39,11 @@ ThreadLocalPointer<const RunLoop::RunLoopTimeout>& RunLoopTimeoutTLS() {
return *tls;
}
void OnRunLoopTimeout(RunLoop* run_loop, OnceClosure on_timeout) {
void OnRunLoopTimeout(RunLoop* run_loop,
const Location& location,
OnceCallback<void(const Location&)> on_timeout) {
run_loop->Quit();
std::move(on_timeout).Run();
std::move(on_timeout).Run(location);
}
} // namespace
@ -106,7 +108,7 @@ RunLoop::~RunLoop() {
DCHECK(!running_);
}
void RunLoop::Run() {
void RunLoop::Run(const Location& location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!BeforeRun())
@ -118,8 +120,8 @@ void RunLoop::Run() {
CancelableOnceClosure cancelable_timeout;
const RunLoopTimeout* run_timeout = GetTimeoutForCurrentThread();
if (run_timeout) {
cancelable_timeout.Reset(
BindOnce(&OnRunLoopTimeout, Unretained(this), run_timeout->on_timeout));
cancelable_timeout.Reset(BindOnce(&OnRunLoopTimeout, Unretained(this),
location, run_timeout->on_timeout));
origin_task_runner_->PostDelayedTask(
FROM_HERE, cancelable_timeout.callback(), run_timeout->timeout);
}

@ -13,6 +13,7 @@
#include "base/callback.h"
#include "base/containers/stack.h"
#include "base/gtest_prod_util.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
@ -81,7 +82,7 @@ class BASE_EXPORT RunLoop {
// Run the current RunLoop::Delegate. This blocks until Quit is called
// (directly or by running the RunLoop::QuitClosure).
void Run();
void Run(const Location& location = Location::Current());
// Run the current RunLoop::Delegate until it doesn't find any tasks or
// messages in its queue (it goes idle).
@ -281,7 +282,7 @@ class BASE_EXPORT RunLoop {
RunLoopTimeout();
~RunLoopTimeout();
TimeDelta timeout;
RepeatingClosure on_timeout;
RepeatingCallback<void(const Location&)> on_timeout;
};
private:

@ -414,7 +414,8 @@ TEST_F(TaskAnnotatorBacktraceIntegrationTest, SingleThreadedNested) {
{
TaskAnnotator::ScopedSetIpcHash scoped_ipc_hash(dummy_ipc_hash2);
ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, BindOnce(&RunLoop::Run, Unretained(&nested_run_loop1)));
FROM_HERE,
BindOnce(&RunLoop::Run, Unretained(&nested_run_loop1), FROM_HERE));
}
run_loop.Run();

@ -9,6 +9,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@ -19,8 +20,10 @@ namespace {
bool g_add_gtest_failure_on_timeout = false;
std::string TimeoutMessage(const RepeatingCallback<std::string()>& get_log) {
std::string message = "RunLoop::Run() timed out.";
std::string TimeoutMessage(const RepeatingCallback<std::string()>& get_log,
const Location& timeout_enabled_from_here) {
std::string message = "RunLoop::Run() timed out. Timeout set at ";
message += timeout_enabled_from_here.ToString() + ".";
if (get_log)
StrAppend(&message, {"\n", get_log.Run()});
return message;
@ -37,7 +40,7 @@ ScopedRunLoopTimeout::~ScopedRunLoopTimeout() {
}
ScopedRunLoopTimeout::ScopedRunLoopTimeout(
const Location& from_here,
const Location& timeout_enabled_from_here,
TimeDelta timeout,
RepeatingCallback<std::string()> on_timeout_log)
: nested_timeout_(RunLoop::GetTimeoutForCurrentThread()) {
@ -46,21 +49,24 @@ ScopedRunLoopTimeout::ScopedRunLoopTimeout(
if (g_add_gtest_failure_on_timeout) {
run_timeout_.on_timeout = BindRepeating(
[](const Location& from_here,
RepeatingCallback<std::string()> on_timeout_log) {
GTEST_FAIL_AT(from_here.file_name(), from_here.line_number())
<< TimeoutMessage(on_timeout_log);
[](const Location& timeout_enabled_from_here,
RepeatingCallback<std::string()> on_timeout_log,
const Location& run_from_here) {
GTEST_FAIL_AT(run_from_here.file_name(), run_from_here.line_number())
<< TimeoutMessage(on_timeout_log, timeout_enabled_from_here);
},
from_here, std::move(on_timeout_log));
timeout_enabled_from_here, std::move(on_timeout_log));
} else {
run_timeout_.on_timeout = BindRepeating(
[](const Location& from_here,
RepeatingCallback<std::string()> on_timeout_log) {
std::string message = TimeoutMessage(on_timeout_log);
logging::LogMessage(from_here.file_name(), from_here.line_number(),
message.data());
[](const Location& timeout_enabled_from_here,
RepeatingCallback<std::string()> on_timeout_log,
const Location& run_from_here) {
std::string message =
TimeoutMessage(on_timeout_log, timeout_enabled_from_here);
logging::LogMessage(run_from_here.file_name(),
run_from_here.line_number(), message.data());
},
from_here, std::move(on_timeout_log));
timeout_enabled_from_here, std::move(on_timeout_log));
}
RunLoop::SetTimeoutForCurrentThread(&run_timeout_);

@ -58,12 +58,13 @@ FORWARD_DECLARE_TEST(TaskEnvironmentTest, SetsDefaultRunTimeout);
class ScopedRunLoopTimeout {
public:
ScopedRunLoopTimeout(const Location& from_here, TimeDelta timeout);
ScopedRunLoopTimeout(const Location& timeout_enabled_from_here,
TimeDelta timeout);
~ScopedRunLoopTimeout();
// Invokes |on_timeout_log| if |timeout| expires, and appends it to the
// logged error message.
ScopedRunLoopTimeout(const Location& from_here,
ScopedRunLoopTimeout(const Location& timeout_enabled_from_here,
TimeDelta timeout,
RepeatingCallback<std::string()> on_timeout_log);

@ -72,7 +72,11 @@ TEST(ScopedRunLoopTimeoutTest, OnTimeoutLog) {
// EXPECT_FATAL_FAILURE() can only reference globals and statics.
static RunLoop& static_loop = run_loop;
EXPECT_FATAL_FAILURE(static_loop.Run(), "Run() timed out.\nI like kittens!");
EXPECT_FATAL_FAILURE(
static_loop.Run(),
"Run() timed out. Timeout set at "
"TestBody@../../base/test/scoped_run_loop_timeout_unittest.cc:70.\n"
"I like kittens!");
}
} // namespace test

@ -880,8 +880,12 @@ TEST_F(TaskEnvironmentTest, SetsDefaultRunTimeout) {
if (!debug::BeingDebugged()) {
EXPECT_LT(run_timeout->timeout, TestTimeouts::test_launcher_timeout());
}
static const RepeatingClosure& static_on_timeout = run_timeout->on_timeout;
EXPECT_FATAL_FAILURE(static_on_timeout.Run(), "RunLoop::Run() timed out");
static auto& static_on_timeout_cb = run_timeout->on_timeout;
EXPECT_FATAL_FAILURE(
static_on_timeout_cb.Run(FROM_HERE),
"RunLoop::Run() timed out. Timeout set at "
// We don't test the line number but it would be present.
"TaskEnvironment@../../base/test/task_environment.cc:");
}
EXPECT_EQ(ScopedRunLoopTimeout::GetTimeoutForCurrentThread(),

@ -353,9 +353,12 @@ IN_PROC_BROWSER_TEST_F(ContentBrowserTest, RunTimeoutInstalled) {
EXPECT_TRUE(run_timeout);
EXPECT_LT(run_timeout->timeout, TestTimeouts::test_launcher_timeout());
static const base::RepeatingClosure& static_on_timeout =
run_timeout->on_timeout;
EXPECT_FATAL_FAILURE(static_on_timeout.Run(), "RunLoop::Run() timed out");
static auto& static_on_timeout_cb = run_timeout->on_timeout;
EXPECT_FATAL_FAILURE(static_on_timeout_cb.Run(FROM_HERE),
"RunLoop::Run() timed out. Timeout set at "
// We don't test the line number but it would be present.
"ProxyRunTestOnMainThreadLoop@../../content/public/test/"
"browser_test_base.cc:");
}
} // namespace content

@ -114,8 +114,8 @@ int32_t MessageLoopResource::Run() {
run_loop_ = &run_loop;
nested_invocations_++;
CallWhileUnlocked(
base::BindOnce(&base::RunLoop::Run, base::Unretained(run_loop_)));
CallWhileUnlocked(base::BindOnce(&base::RunLoop::Run,
base::Unretained(run_loop_), FROM_HERE));
nested_invocations_--;
run_loop_ = previous_run_loop;