Commit 3b7428e5 authored by Greg Thompson's avatar Greg Thompson Committed by Commit Bot

Improve temp dir handling for child procs on Windows.

Tests may leak files/directories in temp due to ScopedTempDir lifetime
issues (see linked bug) or as a result of crashes. This CL augments the
TestLauncher to handle such leaks a bit better. When launching a child
proc on Windows, the TestLauncher now directs that child to its own
dedicated temporary directory. Upon child exit, the launcher checks for
leaked files/directories in that dir and deletes the dir. On test
iteration completion, the launcher will emit a list of the tests that
produced leaks when run with --test-launcher-print-temp-leaks that looks
a lot like this:

ERROR: 2 files and/or directories were left behind in the temporary directory by one or more of these tests: PaymentManagerTest.ClearPaymentInstruments:PermissionControllerImplTest.ResettingOverridesForwardsReset:PermissionControllerImplTest.SettingOverridesForwardsUpdates:PermissionControllerImplTest.RequestPermissionsDelegatesIffMissingOverrides:PermissionControllerImplTest.GetPermissionStatusDelegatesIffNoOverrides:PermissionControllerImplTest.GetPermissionStatusForFrameDelegatesIffNoOverrides:PermissionControllerImplTest.NotifyChangedSubscriptionsCallsOnChangeOnly:PermissionControllerImplTest.PermissionsCannotBeOverriddenIfNotOverridable:PermissionControllerImplTest.GrantPermissionsReturnsStatusesBeingSetIfOverridable:PictureInPictureServiceImplTest.EnterPictureInPicture

Curious developers can binary search in that short list of tests to find
the one(s) that are leaking and clean them up.

This is a reland of https://crrev.com/728475 with macOS support
disabled and a new unit test.

BUG=546640

Change-Id: I93bef02a6282476b968ebd3938bc4d07357b88d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1991565
Auto-Submit: Greg Thompson <grt@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#729785}
parent 86a2fd76
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/environment.h" #include "base/environment.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
...@@ -465,10 +466,36 @@ struct ChildProcessResults { ...@@ -465,10 +466,36 @@ struct ChildProcessResults {
int exit_code; int exit_code;
}; };
// Returns the path to a temporary directory within |task_temp_dir| for the
// child process of index |child_index|, or an empty FilePath if per-child temp
// dirs are not supported.
FilePath CreateChildTempDirIfSupported(const FilePath& task_temp_dir,
int child_index) {
if (!TestLauncher::SupportsPerChildTempDirs())
return FilePath();
FilePath child_temp = task_temp_dir.AppendASCII(NumberToString(child_index));
CHECK(CreateDirectoryAndGetError(child_temp, nullptr));
return child_temp;
}
// Adds the platform-specific variable setting |temp_dir| as a process's
// temporary directory to |environment|.
void SetTemporaryDirectory(const FilePath& temp_dir,
EnvironmentMap* environment) {
#if defined(OS_WIN)
environment->emplace(L"TMP", temp_dir.value());
#elif defined(OS_MACOSX)
environment->emplace("MAC_CHROMIUM_TMPDIR", temp_dir.value());
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
environment->emplace("TMPDIR", temp_dir.value());
#endif
}
// This launches the child test process, waits for it to complete, // This launches the child test process, waits for it to complete,
// and returns child process results. // and returns child process results.
ChildProcessResults DoLaunchChildTestProcess( ChildProcessResults DoLaunchChildTestProcess(
const CommandLine& command_line, const CommandLine& command_line,
const FilePath& process_temp_dir,
TimeDelta timeout, TimeDelta timeout,
const TestLauncher::LaunchOptions& test_launch_options, const TestLauncher::LaunchOptions& test_launch_options,
bool redirect_stdio, bool redirect_stdio,
...@@ -486,6 +513,10 @@ ChildProcessResults DoLaunchChildTestProcess( ...@@ -486,6 +513,10 @@ ChildProcessResults DoLaunchChildTestProcess(
} }
LaunchOptions options; LaunchOptions options;
// Tell the child process to use its designated temporary directory.
if (!process_temp_dir.empty())
SetTemporaryDirectory(process_temp_dir, &options.environment);
#if defined(OS_WIN) #if defined(OS_WIN)
options.inherit_mode = test_launch_options.inherit_mode; options.inherit_mode = test_launch_options.inherit_mode;
...@@ -581,25 +612,26 @@ class TestRunner { ...@@ -581,25 +612,26 @@ class TestRunner {
private: private:
// Called to check if the next batch has to run on the same // Called to check if the next batch has to run on the same
// sequence task runner and using the same temporary directory. // sequence task runner and using the same temporary directory.
bool ShouldReuseStateFromLastBatch( static bool ShouldReuseStateFromLastBatch(
const std::vector<std::string>& test_names) { const std::vector<std::string>& test_names) {
return test_names.size() == 1u && return test_names.size() == 1u &&
test_names.front().find(kPreTestPrefix) != std::string::npos; test_names.front().find(kPreTestPrefix) != std::string::npos;
} }
// Launch next child process on |task_runner|, // Launches the next child process on |task_runner| and clears
// and clear |temp_dir| from previous process. // |last_task_temp_dir| from the previous task.
void LaunchNextTask(scoped_refptr<TaskRunner> task_runner, FilePath temp_dir); void LaunchNextTask(scoped_refptr<TaskRunner> task_runner,
const FilePath& last_task_temp_dir);
// Forward |temp_dir| and Launch next task on main thread. // Forwards |last_task_temp_dir| and launches the next task on main thread.
// The method is called on |task_runner|. // The method is called on |task_runner|.
void ClearAndLaunchNext(scoped_refptr<TaskRunner> main_thread_runner, void ClearAndLaunchNext(scoped_refptr<TaskRunner> main_thread_runner,
scoped_refptr<TaskRunner> task_runner, scoped_refptr<TaskRunner> task_runner,
const FilePath& temp_dir) { const FilePath& last_task_temp_dir) {
main_thread_runner->PostTask( main_thread_runner->PostTask(
FROM_HERE, FROM_HERE,
BindOnce(&TestRunner::LaunchNextTask, weak_ptr_factory_.GetWeakPtr(), BindOnce(&TestRunner::LaunchNextTask, weak_ptr_factory_.GetWeakPtr(),
task_runner, temp_dir)); task_runner, last_task_temp_dir));
} }
ThreadChecker thread_checker_; ThreadChecker thread_checker_;
...@@ -639,12 +671,13 @@ void TestRunner::Run(const std::vector<std::string>& test_names) { ...@@ -639,12 +671,13 @@ void TestRunner::Run(const std::vector<std::string>& test_names) {
} }
void TestRunner::LaunchNextTask(scoped_refptr<TaskRunner> task_runner, void TestRunner::LaunchNextTask(scoped_refptr<TaskRunner> task_runner,
FilePath temp_dir) { const FilePath& last_task_temp_dir) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
// delete previous temporary directory // delete previous temporary directory
if (!temp_dir.empty() && !DeleteFileRecursively(temp_dir)) { if (!last_task_temp_dir.empty() &&
!DeleteFileRecursively(last_task_temp_dir)) {
// This needs to be non-fatal at least for Windows. // This needs to be non-fatal at least for Windows.
LOG(WARNING) << "Failed to delete " << temp_dir.AsUTF8Unsafe(); LOG(WARNING) << "Failed to delete " << last_task_temp_dir.AsUTF8Unsafe();
} }
// No more tests to run, finish sequence. // No more tests to run, finish sequence.
...@@ -656,10 +689,17 @@ void TestRunner::LaunchNextTask(scoped_refptr<TaskRunner> task_runner, ...@@ -656,10 +689,17 @@ void TestRunner::LaunchNextTask(scoped_refptr<TaskRunner> task_runner,
return; return;
} }
CreateNewTempDirectory(FilePath::StringType(), &temp_dir); // Create a temporary directory for this task. This directory will hold the
// flags and results files for the child processes as well as their User Data
// dir, where appropriate. For platforms that support per-child temp dirs,
// this directory will also contain one subdirectory per child for that
// child's process-wide temp dir.
base::FilePath task_temp_dir;
CHECK(CreateNewTempDirectory(FilePath::StringType(), &task_temp_dir));
bool post_to_current_runner = true; bool post_to_current_runner = true;
size_t batch_size = (batch_size_ == 0) ? tests_to_run_.size() : batch_size_; size_t batch_size = (batch_size_ == 0) ? tests_to_run_.size() : batch_size_;
int child_index = 0;
while (post_to_current_runner && !tests_to_run_.empty()) { while (post_to_current_runner && !tests_to_run_.empty()) {
batch_size = std::min(batch_size, tests_to_run_.size()); batch_size = std::min(batch_size, tests_to_run_.size());
std::vector<std::string> batch(tests_to_run_.rbegin(), std::vector<std::string> batch(tests_to_run_.rbegin(),
...@@ -668,13 +708,29 @@ void TestRunner::LaunchNextTask(scoped_refptr<TaskRunner> task_runner, ...@@ -668,13 +708,29 @@ void TestRunner::LaunchNextTask(scoped_refptr<TaskRunner> task_runner,
task_runner->PostTask( task_runner->PostTask(
FROM_HERE, FROM_HERE,
BindOnce(&TestLauncher::LaunchChildGTestProcess, Unretained(launcher_), BindOnce(&TestLauncher::LaunchChildGTestProcess, Unretained(launcher_),
ThreadTaskRunnerHandle::Get(), batch, temp_dir)); ThreadTaskRunnerHandle::Get(), batch, task_temp_dir,
CreateChildTempDirIfSupported(task_temp_dir, child_index++)));
post_to_current_runner = ShouldReuseStateFromLastBatch(batch); post_to_current_runner = ShouldReuseStateFromLastBatch(batch);
} }
task_runner->PostTask( task_runner->PostTask(
FROM_HERE, FROM_HERE,
BindOnce(&TestRunner::ClearAndLaunchNext, Unretained(this), BindOnce(&TestRunner::ClearAndLaunchNext, Unretained(this),
ThreadTaskRunnerHandle::Get(), task_runner, temp_dir)); ThreadTaskRunnerHandle::Get(), task_runner, task_temp_dir));
}
// Returns the number of files and directories in |dir|, or 0 if |dir| is empty.
int CountItemsInDirectory(const FilePath& dir) {
if (dir.empty())
return 0;
int items = 0;
FileEnumerator file_enumerator(
dir, /*recursive=*/false,
FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
for (FilePath name = file_enumerator.Next(); !name.empty();
name = file_enumerator.Next()) {
++items;
}
return items;
} }
} // namespace } // namespace
...@@ -868,10 +924,11 @@ bool TestLauncher::Run(CommandLine* command_line) { ...@@ -868,10 +924,11 @@ bool TestLauncher::Run(CommandLine* command_line) {
void TestLauncher::LaunchChildGTestProcess( void TestLauncher::LaunchChildGTestProcess(
scoped_refptr<TaskRunner> task_runner, scoped_refptr<TaskRunner> task_runner,
const std::vector<std::string>& test_names, const std::vector<std::string>& test_names,
const FilePath& temp_dir) { const FilePath& task_temp_dir,
const FilePath& child_temp_dir) {
FilePath result_file; FilePath result_file;
CommandLine cmd_line = CommandLine cmd_line = launcher_delegate_->GetCommandLine(
launcher_delegate_->GetCommandLine(test_names, temp_dir, &result_file); test_names, task_temp_dir, &result_file);
// Record the exact command line used to launch the child. // Record the exact command line used to launch the child.
CommandLine new_command_line( CommandLine new_command_line(
...@@ -880,8 +937,9 @@ void TestLauncher::LaunchChildGTestProcess( ...@@ -880,8 +937,9 @@ void TestLauncher::LaunchChildGTestProcess(
options.flags = launcher_delegate_->GetLaunchOptions(); options.flags = launcher_delegate_->GetLaunchOptions();
ChildProcessResults process_results = DoLaunchChildTestProcess( ChildProcessResults process_results = DoLaunchChildTestProcess(
new_command_line, launcher_delegate_->GetTimeout() * test_names.size(), new_command_line, child_temp_dir,
options, redirect_stdio_, launcher_delegate_); launcher_delegate_->GetTimeout() * test_names.size(), options,
redirect_stdio_, launcher_delegate_);
// Invoke ProcessTestResults on the original thread, not // Invoke ProcessTestResults on the original thread, not
// on a worker pool thread. // on a worker pool thread.
...@@ -890,7 +948,8 @@ void TestLauncher::LaunchChildGTestProcess( ...@@ -890,7 +948,8 @@ void TestLauncher::LaunchChildGTestProcess(
BindOnce(&TestLauncher::ProcessTestResults, Unretained(this), test_names, BindOnce(&TestLauncher::ProcessTestResults, Unretained(this), test_names,
result_file, process_results.output_file_contents, result_file, process_results.output_file_contents,
process_results.elapsed_time, process_results.exit_code, process_results.elapsed_time, process_results.exit_code,
process_results.was_timeout)); process_results.was_timeout,
CountItemsInDirectory(child_temp_dir)));
} }
// Determines which result status will be assigned for missing test results. // Determines which result status will be assigned for missing test results.
...@@ -920,7 +979,8 @@ void TestLauncher::ProcessTestResults( ...@@ -920,7 +979,8 @@ void TestLauncher::ProcessTestResults(
const std::string& output, const std::string& output,
TimeDelta elapsed_time, TimeDelta elapsed_time,
int exit_code, int exit_code,
bool was_timeout) { bool was_timeout,
int leaked_items) {
std::vector<TestResult> test_results; std::vector<TestResult> test_results;
bool crashed = false; bool crashed = false;
bool have_test_results = bool have_test_results =
...@@ -1000,6 +1060,9 @@ void TestLauncher::ProcessTestResults( ...@@ -1000,6 +1060,9 @@ void TestLauncher::ProcessTestResults(
i.output_snippet = GetTestOutputSnippet(i, output); i.output_snippet = GetTestOutputSnippet(i, output);
} }
if (leaked_items)
results_tracker_.AddLeakedItems(leaked_items, test_names);
launcher_delegate_->ProcessTestResults(final_results, elapsed_time); launcher_delegate_->ProcessTestResults(final_results, elapsed_time);
for (const auto& result : final_results) for (const auto& result : final_results)
......
...@@ -135,18 +135,32 @@ class TestLauncher { ...@@ -135,18 +135,32 @@ class TestLauncher {
// Launches a child process (assumed to be gtest-based binary) which runs // Launches a child process (assumed to be gtest-based binary) which runs
// tests indicated by |test_names|. // tests indicated by |test_names|.
// |task_runner| is used to post results back to the launcher // |task_runner| is used to post results back to the launcher on the main
// on the main thread. |temp_dir| is used for child process files, // thread. |task_temp_dir| is used for child process files such as user data,
// such as user data, result file, and flag_file. // result file, and flag_file. |child_temp_dir|, if not empty, specifies a
// directory (within task_temp_dir) that the child process will use as its
// process-wide temporary directory.
// virtual to mock in testing. // virtual to mock in testing.
virtual void LaunchChildGTestProcess( virtual void LaunchChildGTestProcess(
scoped_refptr<TaskRunner> task_runner, scoped_refptr<TaskRunner> task_runner,
const std::vector<std::string>& test_names, const std::vector<std::string>& test_names,
const FilePath& temp_dir); const FilePath& task_temp_dir,
const FilePath& child_temp_dir);
// Called when a test has finished running. // Called when a test has finished running.
void OnTestFinished(const TestResult& result); void OnTestFinished(const TestResult& result);
// Returns true if child test processes should have dedicated temporary
// directories.
static constexpr bool SupportsPerChildTempDirs() {
#if defined(OS_WIN)
return true;
#else
// TODO(https://crbug.com/1038857): Enable for macOS, Linux, and Fuchsia.
return false;
#endif
}
private: private:
bool Init(CommandLine* command_line) WARN_UNUSED_RESULT; bool Init(CommandLine* command_line) WARN_UNUSED_RESULT;
...@@ -206,12 +220,15 @@ class TestLauncher { ...@@ -206,12 +220,15 @@ class TestLauncher {
// EXPECT/ASSERT/DCHECK statements. Test launcher parses that // EXPECT/ASSERT/DCHECK statements. Test launcher parses that
// file to get additional information about test run (status, // file to get additional information about test run (status,
// error-messages, stack-traces and file/line for failures). // error-messages, stack-traces and file/line for failures).
// |leaked_items| is the number of files and/or directories remaining in the
// child process's temporary directory upon its termination.
void ProcessTestResults(const std::vector<std::string>& test_names, void ProcessTestResults(const std::vector<std::string>& test_names,
const FilePath& result_file, const FilePath& result_file,
const std::string& output, const std::string& output,
TimeDelta elapsed_time, TimeDelta elapsed_time,
int exit_code, int exit_code,
bool was_timeout); bool was_timeout,
int leaked_items);
// Make sure we don't accidentally call the wrong methods e.g. on the worker // Make sure we don't accidentally call the wrong methods e.g. on the worker
// pool thread. Should be the first member so that it's destroyed last: when // pool thread. Should be the first member so that it's destroyed last: when
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/test/gtest_util.h" #include "base/test/gtest_util.h"
#include "base/test/launcher/test_launcher.h" #include "base/test/launcher/test_launcher.h"
#include "base/test/test_switches.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/values.h" #include "base/values.h"
...@@ -166,6 +167,9 @@ bool TestResultsTracker::Init(const CommandLine& command_line) { ...@@ -166,6 +167,9 @@ bool TestResultsTracker::Init(const CommandLine& command_line) {
return false; return false;
} }
print_temp_leaks_ =
command_line.HasSwitch(switches::kTestLauncherPrintTempLeaks);
if (!command_line.HasSwitch(kGTestOutputFlag)) if (!command_line.HasSwitch(kGTestOutputFlag))
return true; return true;
...@@ -270,6 +274,13 @@ void TestResultsTracker::AddTestResult(const TestResult& result) { ...@@ -270,6 +274,13 @@ void TestResultsTracker::AddTestResult(const TestResult& result) {
aggregate_test_result.test_results.push_back(result); aggregate_test_result.test_results.push_back(result);
} }
void TestResultsTracker::AddLeakedItems(
int count,
const std::vector<std::string>& test_names) {
DCHECK(count);
per_iteration_data_.back().leaked_temp_items.emplace_back(count, test_names);
}
void TestResultsTracker::GeneratePlaceholderIteration() { void TestResultsTracker::GeneratePlaceholderIteration() {
CHECK(thread_checker_.CalledOnValidThread()); CHECK(thread_checker_.CalledOnValidThread());
...@@ -316,6 +327,13 @@ void TestResultsTracker::PrintSummaryOfCurrentIteration() const { ...@@ -316,6 +327,13 @@ void TestResultsTracker::PrintSummaryOfCurrentIteration() const {
"had unknown result"); "had unknown result");
PrintTests(tests_by_status[TestResult::TEST_NOT_RUN].begin(), PrintTests(tests_by_status[TestResult::TEST_NOT_RUN].begin(),
tests_by_status[TestResult::TEST_NOT_RUN].end(), "not run"); tests_by_status[TestResult::TEST_NOT_RUN].end(), "not run");
if (print_temp_leaks_) {
for (const auto& leaking_tests :
per_iteration_data_.back().leaked_temp_items) {
PrintLeaks(leaking_tests.first, leaking_tests.second);
}
}
} }
void TestResultsTracker::PrintSummaryOfAllIterations() const { void TestResultsTracker::PrintSummaryOfAllIterations() const {
...@@ -574,6 +592,16 @@ void TestResultsTracker::PrintTests(InputIterator first, ...@@ -574,6 +592,16 @@ void TestResultsTracker::PrintTests(InputIterator first,
fflush(stdout); fflush(stdout);
} }
void TestResultsTracker::PrintLeaks(
int count,
const std::vector<std::string>& test_names) const {
fprintf(stdout,
"ERROR: %d files and/or directories were left behind in the temporary"
" directory by one or more of these tests: %s\n",
count, JoinString(test_names, ":").c_str());
fflush(stdout);
}
TestResultsTracker::AggregateTestResult::AggregateTestResult() = default; TestResultsTracker::AggregateTestResult::AggregateTestResult() = default;
TestResultsTracker::AggregateTestResult::AggregateTestResult( TestResultsTracker::AggregateTestResult::AggregateTestResult(
......
...@@ -61,6 +61,10 @@ class TestResultsTracker { ...@@ -61,6 +61,10 @@ class TestResultsTracker {
// Adds |result| to the stored test results. // Adds |result| to the stored test results.
void AddTestResult(const TestResult& result); void AddTestResult(const TestResult& result);
// Adds to the current iteration the fact that |count| items were leaked by
// one or more tests in |test_names| in its temporary directory.
void AddLeakedItems(int count, const std::vector<std::string>& test_names);
// Even when no iterations have occurred, we still want to generate output // Even when no iterations have occurred, we still want to generate output
// data with "NOTRUN" status for each test. This method generates a // data with "NOTRUN" status for each test. This method generates a
// placeholder iteration. The first iteration will overwrite the data in the // placeholder iteration. The first iteration will overwrite the data in the
...@@ -101,6 +105,7 @@ class TestResultsTracker { ...@@ -101,6 +105,7 @@ class TestResultsTracker {
void PrintTests(InputIterator first, void PrintTests(InputIterator first,
InputIterator last, InputIterator last,
const std::string& description) const; const std::string& description) const;
void PrintLeaks(int count, const std::vector<std::string>& test_names) const;
struct AggregateTestResult { struct AggregateTestResult {
AggregateTestResult(); AggregateTestResult();
...@@ -118,6 +123,9 @@ class TestResultsTracker { ...@@ -118,6 +123,9 @@ class TestResultsTracker {
// Aggregate test results grouped by full test name. // Aggregate test results grouped by full test name.
typedef std::map<std::string, AggregateTestResult> ResultsMap; typedef std::map<std::string, AggregateTestResult> ResultsMap;
ResultsMap results; ResultsMap results;
// A sequence of tests that leaked files/dirs in their temp directory.
std::vector<std::pair<int, std::vector<std::string>>> leaked_temp_items;
}; };
struct CodeLocation { struct CodeLocation {
...@@ -130,6 +138,9 @@ class TestResultsTracker { ...@@ -130,6 +138,9 @@ class TestResultsTracker {
ThreadChecker thread_checker_; ThreadChecker thread_checker_;
// Print tests that leak files and/or directories in their temp dir.
bool print_temp_leaks_ = false;
// Set of global tags, i.e. strings indicating conditions that apply to // Set of global tags, i.e. strings indicating conditions that apply to
// the entire test run. // the entire test run.
std::set<std::string> global_tags_; std::set<std::string> global_tags_;
......
...@@ -104,7 +104,11 @@ void PrintUsage() { ...@@ -104,7 +104,11 @@ void PrintUsage() {
" Sets the shard index to run to N (from 0 to TOTAL - 1).\n" " Sets the shard index to run to N (from 0 to TOTAL - 1).\n"
"\n" "\n"
" --dont-use-job-objects\n" " --dont-use-job-objects\n"
" Avoids using job objects in Windows.\n"); " Avoids using job objects in Windows.\n"
"\n"
" --test-launcher-print-temp-leaks\n"
" Prints information about leaked files and/or directories in\n"
" child process's temporary directories (Windows and macOS).\n");
fflush(stdout); fflush(stdout);
} }
......
...@@ -58,6 +58,11 @@ const char switches::kIsolatedScriptTestLauncherRetryLimit[] = ...@@ -58,6 +58,11 @@ const char switches::kIsolatedScriptTestLauncherRetryLimit[] =
const char switches::kTestLauncherSummaryOutput[] = const char switches::kTestLauncherSummaryOutput[] =
"test-launcher-summary-output"; "test-launcher-summary-output";
// Causes the test launcher to print information about leaked files and/or
// directories in child process's temporary directories.
const char switches::kTestLauncherPrintTempLeaks[] =
"test-launcher-print-temp-leaks";
// Flag controlling when test stdio is displayed as part of the launcher's // Flag controlling when test stdio is displayed as part of the launcher's
// standard output. // standard output.
const char switches::kTestLauncherPrintTestStdio[] = const char switches::kTestLauncherPrintTestStdio[] =
......
...@@ -25,6 +25,7 @@ extern const char kTestLauncherOutput[]; ...@@ -25,6 +25,7 @@ extern const char kTestLauncherOutput[];
extern const char kTestLauncherRetryLimit[]; extern const char kTestLauncherRetryLimit[];
extern const char kIsolatedScriptTestLauncherRetryLimit[]; extern const char kIsolatedScriptTestLauncherRetryLimit[];
extern const char kTestLauncherSummaryOutput[]; extern const char kTestLauncherSummaryOutput[];
extern const char kTestLauncherPrintTempLeaks[];
extern const char kTestLauncherPrintTestStdio[]; extern const char kTestLauncherPrintTestStdio[];
extern const char kTestLauncherPrintWritablePath[]; extern const char kTestLauncherPrintWritablePath[];
extern const char kTestLauncherShardIndex[]; extern const char kTestLauncherShardIndex[];
......
...@@ -116,7 +116,11 @@ void PrintUsage() { ...@@ -116,7 +116,11 @@ void PrintUsage() {
" Sets the total number of shards to N.\n" " Sets the total number of shards to N.\n"
"\n" "\n"
" --test-launcher-shard-index=N\n" " --test-launcher-shard-index=N\n"
" Sets the shard index to run to N (from 0 to TOTAL - 1).\n"); " Sets the shard index to run to N (from 0 to TOTAL - 1).\n"
"\n"
" --test-launcher-print-temp-leaks\n"
" Prints information about leaked files and/or directories in\n"
" child process's temporary directories (Windows and macOS).\n");
} }
// Implementation of base::TestLauncherDelegate. This is also a test launcher, // Implementation of base::TestLauncherDelegate. This is also a test launcher,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment