Index: base/test/launcher/unit_test_launcher.cc |
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc |
index 2a4e74877bd3335101be1fc440ee87aa027296b9..1b3a162c661ac25f419ed477764f5d935b467221 100644 |
--- a/base/test/launcher/unit_test_launcher.cc |
+++ b/base/test/launcher/unit_test_launcher.cc |
@@ -121,6 +121,18 @@ class DefaultUnitTestPlatformDelegate : public UnitTestPlatformDelegate { |
return std::string(); |
} |
+ void RelaunchTests(TestLauncher* test_launcher, |
+ const std::vector<std::string>& test_names, |
+ int launch_flags) override { |
+ // Relaunch requested tests in parallel, but only use single |
+ // test per batch for more precise results (crashes, etc). |
+ for (const std::string& test_name : test_names) { |
+ std::vector<std::string> batch; |
+ batch.push_back(test_name); |
+ RunUnitTestsBatch(test_launcher, this, batch, launch_flags); |
+ } |
+ } |
+ |
DISALLOW_COPY_AND_ASSIGN(DefaultUnitTestPlatformDelegate); |
}; |
@@ -216,230 +228,9 @@ void InitGoogleTestWChar(int* argc, wchar_t** argv) { |
} |
#endif // defined(OS_WIN) |
-} // namespace |
- |
-int LaunchUnitTests(int argc, |
- char** argv, |
- const RunTestSuiteCallback& run_test_suite) { |
- CommandLine::Init(argc, argv); |
- return LaunchUnitTestsInternal(run_test_suite, SysInfo::NumberOfProcessors(), |
- true, Bind(&InitGoogleTestChar, &argc, argv)); |
-} |
- |
-int LaunchUnitTestsSerially(int argc, |
- char** argv, |
- const RunTestSuiteCallback& run_test_suite) { |
- CommandLine::Init(argc, argv); |
- return LaunchUnitTestsInternal(run_test_suite, 1, true, |
- Bind(&InitGoogleTestChar, &argc, argv)); |
-} |
- |
-#if defined(OS_WIN) |
-int LaunchUnitTests(int argc, |
- wchar_t** argv, |
- bool use_job_objects, |
- const RunTestSuiteCallback& run_test_suite) { |
- // Windows CommandLine::Init ignores argv anyway. |
- CommandLine::Init(argc, NULL); |
- return LaunchUnitTestsInternal(run_test_suite, SysInfo::NumberOfProcessors(), |
- use_job_objects, |
- Bind(&InitGoogleTestWChar, &argc, argv)); |
-} |
-#endif // defined(OS_WIN) |
- |
-UnitTestLauncherDelegate::UnitTestLauncherDelegate( |
- UnitTestPlatformDelegate* platform_delegate, |
- size_t batch_limit, |
- bool use_job_objects) |
- : platform_delegate_(platform_delegate), |
- batch_limit_(batch_limit), |
- use_job_objects_(use_job_objects) { |
-} |
- |
-UnitTestLauncherDelegate::~UnitTestLauncherDelegate() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
-} |
- |
-UnitTestLauncherDelegate::GTestCallbackState::GTestCallbackState() { |
-} |
- |
-UnitTestLauncherDelegate::GTestCallbackState::~GTestCallbackState() { |
-} |
- |
-bool UnitTestLauncherDelegate::GetTests(std::vector<SplitTestName>* output) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- return platform_delegate_->GetTests(output); |
-} |
- |
-bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name, |
- const std::string& test_name) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- // There is no additional logic to disable specific tests. |
- return true; |
-} |
- |
-size_t UnitTestLauncherDelegate::RunTests( |
- TestLauncher* test_launcher, |
- const std::vector<std::string>& test_names) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- std::vector<std::string> batch; |
- for (size_t i = 0; i < test_names.size(); i++) { |
- batch.push_back(test_names[i]); |
- |
- // Use 0 to indicate unlimited batch size. |
- if (batch.size() >= batch_limit_ && batch_limit_ != 0) { |
- RunBatch(test_launcher, batch); |
- batch.clear(); |
- } |
- } |
- |
- RunBatch(test_launcher, batch); |
- |
- return test_names.size(); |
-} |
- |
-size_t UnitTestLauncherDelegate::RetryTests( |
- TestLauncher* test_launcher, |
- const std::vector<std::string>& test_names) { |
- MessageLoop::current()->PostTask( |
- FROM_HERE, Bind(&UnitTestLauncherDelegate::RunSerially, Unretained(this), |
- test_launcher, test_names)); |
- return test_names.size(); |
-} |
- |
-void UnitTestLauncherDelegate::RunSerially( |
- TestLauncher* test_launcher, |
- const std::vector<std::string>& test_names) { |
- if (test_names.empty()) |
- return; |
- |
- std::vector<std::string> new_test_names(test_names); |
- std::string test_name(new_test_names.back()); |
- new_test_names.pop_back(); |
- |
- // Create a dedicated temporary directory to store the xml result data |
- // per run to ensure clean state and make it possible to launch multiple |
- // processes in parallel. |
- base::FilePath output_file; |
- CHECK(platform_delegate_->CreateTemporaryFile(&output_file)); |
- |
- std::vector<std::string> current_test_names; |
- current_test_names.push_back(test_name); |
- CommandLine cmd_line(platform_delegate_->GetCommandLineForChildGTestProcess( |
- current_test_names, output_file)); |
- |
- GTestCallbackState callback_state; |
- callback_state.test_launcher = test_launcher; |
- callback_state.test_names = current_test_names; |
- callback_state.output_file = output_file; |
- |
- test_launcher->LaunchChildGTestProcess( |
- cmd_line, |
- platform_delegate_->GetWrapperForChildGTestProcess(), |
- TestTimeouts::test_launcher_timeout(), |
- use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, |
- Bind(&UnitTestLauncherDelegate::SerialGTestCallback, Unretained(this), |
- callback_state, new_test_names)); |
-} |
- |
-void UnitTestLauncherDelegate::RunBatch( |
- TestLauncher* test_launcher, |
- const std::vector<std::string>& test_names) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- if (test_names.empty()) |
- return; |
- |
- // Create a dedicated temporary directory to store the xml result data |
- // per run to ensure clean state and make it possible to launch multiple |
- // processes in parallel. |
- base::FilePath output_file; |
- CHECK(platform_delegate_->CreateTemporaryFile(&output_file)); |
- |
- CommandLine cmd_line(platform_delegate_->GetCommandLineForChildGTestProcess( |
- test_names, output_file)); |
- |
- // Adjust the timeout depending on how many tests we're running |
- // (note that e.g. the last batch of tests will be smaller). |
- // TODO(phajdan.jr): Consider an adaptive timeout, which can change |
- // depending on how many tests ran and how many remain. |
- // Note: do NOT parse child's stdout to do that, it's known to be |
- // unreliable (e.g. buffering issues can mix up the output). |
- base::TimeDelta timeout = |
- test_names.size() * TestTimeouts::test_launcher_timeout(); |
- |
- GTestCallbackState callback_state; |
- callback_state.test_launcher = test_launcher; |
- callback_state.test_names = test_names; |
- callback_state.output_file = output_file; |
- |
- test_launcher->LaunchChildGTestProcess( |
- cmd_line, |
- platform_delegate_->GetWrapperForChildGTestProcess(), |
- timeout, |
- use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, |
- Bind(&UnitTestLauncherDelegate::GTestCallback, Unretained(this), |
- callback_state)); |
-} |
- |
-void UnitTestLauncherDelegate::GTestCallback( |
- const GTestCallbackState& callback_state, |
- int exit_code, |
- const TimeDelta& elapsed_time, |
- bool was_timeout, |
- const std::string& output) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- std::vector<std::string> tests_to_relaunch; |
- ProcessTestResults(callback_state.test_launcher, callback_state.test_names, |
- callback_state.output_file, output, exit_code, was_timeout, |
- &tests_to_relaunch); |
- |
- // Relaunch requested tests in parallel, but only use single |
- // test per batch for more precise results (crashes, test passes |
- // but non-zero exit codes etc). |
- for (size_t i = 0; i < tests_to_relaunch.size(); i++) { |
- std::vector<std::string> batch; |
- batch.push_back(tests_to_relaunch[i]); |
- RunBatch(callback_state.test_launcher, batch); |
- } |
- |
- // The temporary file's directory is also temporary. |
- DeleteFile(callback_state.output_file.DirName(), true); |
-} |
- |
-void UnitTestLauncherDelegate::SerialGTestCallback( |
- const GTestCallbackState& callback_state, |
- const std::vector<std::string>& test_names, |
- int exit_code, |
- const TimeDelta& elapsed_time, |
- bool was_timeout, |
- const std::string& output) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- std::vector<std::string> tests_to_relaunch; |
- bool called_any_callbacks = |
- ProcessTestResults(callback_state.test_launcher, |
- callback_state.test_names, callback_state.output_file, |
- output, exit_code, was_timeout, &tests_to_relaunch); |
- |
- // There is only one test, there cannot be other tests to relaunch |
- // due to a crash. |
- DCHECK(tests_to_relaunch.empty()); |
- |
- // There is only one test, we should have called back with its result. |
- DCHECK(called_any_callbacks); |
- |
- // The temporary file's directory is also temporary. |
- DeleteFile(callback_state.output_file.DirName(), true); |
- |
- MessageLoop::current()->PostTask( |
- FROM_HERE, Bind(&UnitTestLauncherDelegate::RunSerially, Unretained(this), |
- callback_state.test_launcher, test_names)); |
-} |
- |
-// static |
-bool UnitTestLauncherDelegate::ProcessTestResults( |
+// Interprets test results and reports to the test launcher. Returns true |
+// on success. |
+bool ProcessTestResults( |
TestLauncher* test_launcher, |
const std::vector<std::string>& test_names, |
const base::FilePath& output_file, |
@@ -523,24 +314,10 @@ bool UnitTestLauncherDelegate::ProcessTestResults( |
if (!has_non_success_test && exit_code != 0) { |
// This is a bit surprising case: all tests are marked as successful, |
// but the exit code was not zero. This can happen e.g. under memory |
- // tools that report leaks this way. |
- |
- if (final_results.size() == 1) { |
- // Easy case. One test only so we know the non-zero exit code |
- // was caused by that one test. |
- final_results[0].status = TestResult::TEST_FAILURE_ON_EXIT; |
- } else { |
- // Harder case. Discard the results and request relaunching all |
- // tests without batching. This will trigger above branch on |
- // relaunch leading to more precise results. |
- LOG(WARNING) << "Not sure which test caused non-zero exit code, " |
- << "relaunching all of them without batching."; |
- |
- for (size_t i = 0; i < final_results.size(); i++) |
- tests_to_relaunch->push_back(final_results[i].full_name); |
- |
- return false; |
- } |
+ // tools that report leaks this way. Mark all tests as a failure on exit, |
+ // and for more precise info they'd need to be retried serially. |
+ for (size_t i = 0; i < final_results.size(); i++) |
+ final_results[i].status = TestResult::TEST_FAILURE_ON_EXIT; |
} |
for (size_t i = 0; i < final_results.size(); i++) { |
@@ -574,4 +351,239 @@ bool UnitTestLauncherDelegate::ProcessTestResults( |
return called_any_callback; |
} |
+// TODO(phajdan.jr): Pass parameters directly with C++11 variadic templates. |
+struct GTestCallbackState { |
+ TestLauncher* test_launcher; |
+ UnitTestPlatformDelegate* platform_delegate; |
+ std::vector<std::string> test_names; |
+ int launch_flags; |
+ FilePath output_file; |
+}; |
+ |
+void GTestCallback( |
+ const GTestCallbackState& callback_state, |
+ int exit_code, |
+ const TimeDelta& elapsed_time, |
+ bool was_timeout, |
+ const std::string& output) { |
+ std::vector<std::string> tests_to_relaunch; |
+ ProcessTestResults(callback_state.test_launcher, callback_state.test_names, |
+ callback_state.output_file, output, exit_code, was_timeout, |
+ &tests_to_relaunch); |
+ |
+ if (!tests_to_relaunch.empty()) { |
+ callback_state.platform_delegate->RelaunchTests( |
+ callback_state.test_launcher, |
+ tests_to_relaunch, |
+ callback_state.launch_flags); |
+ } |
+ |
+ // The temporary file's directory is also temporary. |
+ DeleteFile(callback_state.output_file.DirName(), true); |
+} |
+ |
+void SerialGTestCallback( |
+ const GTestCallbackState& callback_state, |
+ const std::vector<std::string>& test_names, |
+ int exit_code, |
+ const TimeDelta& elapsed_time, |
+ bool was_timeout, |
+ const std::string& output) { |
+ std::vector<std::string> tests_to_relaunch; |
+ bool called_any_callbacks = |
+ ProcessTestResults(callback_state.test_launcher, |
+ callback_state.test_names, callback_state.output_file, |
+ output, exit_code, was_timeout, &tests_to_relaunch); |
+ |
+ // There is only one test, there cannot be other tests to relaunch |
+ // due to a crash. |
+ DCHECK(tests_to_relaunch.empty()); |
+ |
+ // There is only one test, we should have called back with its result. |
+ DCHECK(called_any_callbacks); |
+ |
+ // The temporary file's directory is also temporary. |
+ DeleteFile(callback_state.output_file.DirName(), true); |
+ |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ Bind(&RunUnitTestsSerially, |
+ callback_state.test_launcher, |
+ callback_state.platform_delegate, |
+ test_names, |
+ callback_state.launch_flags)); |
+} |
+ |
+} // namespace |
+ |
+int LaunchUnitTests(int argc, |
+ char** argv, |
+ const RunTestSuiteCallback& run_test_suite) { |
+ CommandLine::Init(argc, argv); |
+ return LaunchUnitTestsInternal(run_test_suite, SysInfo::NumberOfProcessors(), |
+ true, Bind(&InitGoogleTestChar, &argc, argv)); |
+} |
+ |
+int LaunchUnitTestsSerially(int argc, |
+ char** argv, |
+ const RunTestSuiteCallback& run_test_suite) { |
+ CommandLine::Init(argc, argv); |
+ return LaunchUnitTestsInternal(run_test_suite, 1, true, |
+ Bind(&InitGoogleTestChar, &argc, argv)); |
+} |
+ |
+#if defined(OS_WIN) |
+int LaunchUnitTests(int argc, |
+ wchar_t** argv, |
+ bool use_job_objects, |
+ const RunTestSuiteCallback& run_test_suite) { |
+ // Windows CommandLine::Init ignores argv anyway. |
+ CommandLine::Init(argc, NULL); |
+ return LaunchUnitTestsInternal(run_test_suite, SysInfo::NumberOfProcessors(), |
+ use_job_objects, |
+ Bind(&InitGoogleTestWChar, &argc, argv)); |
+} |
+#endif // defined(OS_WIN) |
+ |
+void RunUnitTestsSerially( |
+ TestLauncher* test_launcher, |
+ UnitTestPlatformDelegate* platform_delegate, |
+ const std::vector<std::string>& test_names, |
+ int launch_flags) { |
+ if (test_names.empty()) |
+ return; |
+ |
+ std::vector<std::string> new_test_names(test_names); |
+ std::string test_name(new_test_names.back()); |
+ new_test_names.pop_back(); |
+ |
+ // Create a dedicated temporary directory to store the xml result data |
+ // per run to ensure clean state and make it possible to launch multiple |
+ // processes in parallel. |
+ base::FilePath output_file; |
+ CHECK(platform_delegate->CreateTemporaryFile(&output_file)); |
+ |
+ std::vector<std::string> current_test_names; |
+ current_test_names.push_back(test_name); |
+ CommandLine cmd_line(platform_delegate->GetCommandLineForChildGTestProcess( |
+ current_test_names, output_file)); |
+ |
+ GTestCallbackState callback_state; |
+ callback_state.test_launcher = test_launcher; |
+ callback_state.platform_delegate = platform_delegate; |
+ callback_state.test_names = current_test_names; |
+ callback_state.launch_flags = launch_flags; |
+ callback_state.output_file = output_file; |
+ |
+ test_launcher->LaunchChildGTestProcess( |
+ cmd_line, |
+ platform_delegate->GetWrapperForChildGTestProcess(), |
+ TestTimeouts::test_launcher_timeout(), |
+ launch_flags, |
+ Bind(&SerialGTestCallback, callback_state, new_test_names)); |
+} |
+ |
+void RunUnitTestsBatch( |
+ TestLauncher* test_launcher, |
+ UnitTestPlatformDelegate* platform_delegate, |
+ const std::vector<std::string>& test_names, |
+ int launch_flags) { |
+ if (test_names.empty()) |
+ return; |
+ |
+ // Create a dedicated temporary directory to store the xml result data |
+ // per run to ensure clean state and make it possible to launch multiple |
+ // processes in parallel. |
+ base::FilePath output_file; |
+ CHECK(platform_delegate->CreateTemporaryFile(&output_file)); |
+ |
+ CommandLine cmd_line(platform_delegate->GetCommandLineForChildGTestProcess( |
+ test_names, output_file)); |
+ |
+ // Adjust the timeout depending on how many tests we're running |
+ // (note that e.g. the last batch of tests will be smaller). |
+ // TODO(phajdan.jr): Consider an adaptive timeout, which can change |
+ // depending on how many tests ran and how many remain. |
+ // Note: do NOT parse child's stdout to do that, it's known to be |
+ // unreliable (e.g. buffering issues can mix up the output). |
+ base::TimeDelta timeout = |
+ test_names.size() * TestTimeouts::test_launcher_timeout(); |
+ |
+ GTestCallbackState callback_state; |
+ callback_state.test_launcher = test_launcher; |
+ callback_state.platform_delegate = platform_delegate; |
+ callback_state.test_names = test_names; |
+ callback_state.launch_flags = launch_flags; |
+ callback_state.output_file = output_file; |
+ |
+ test_launcher->LaunchChildGTestProcess( |
+ cmd_line, |
+ platform_delegate->GetWrapperForChildGTestProcess(), |
+ timeout, |
+ launch_flags, |
+ Bind(>estCallback, callback_state)); |
+} |
+ |
+UnitTestLauncherDelegate::UnitTestLauncherDelegate( |
+ UnitTestPlatformDelegate* platform_delegate, |
+ size_t batch_limit, |
+ bool use_job_objects) |
+ : platform_delegate_(platform_delegate), |
+ batch_limit_(batch_limit), |
+ use_job_objects_(use_job_objects) { |
+} |
+ |
+UnitTestLauncherDelegate::~UnitTestLauncherDelegate() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+} |
+ |
+bool UnitTestLauncherDelegate::GetTests(std::vector<SplitTestName>* output) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ return platform_delegate_->GetTests(output); |
+} |
+ |
+bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name, |
+ const std::string& test_name) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ // There is no additional logic to disable specific tests. |
+ return true; |
+} |
+ |
+size_t UnitTestLauncherDelegate::RunTests( |
+ TestLauncher* test_launcher, |
+ const std::vector<std::string>& test_names) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+ int launch_flags = use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0; |
+ |
+ std::vector<std::string> batch; |
+ for (size_t i = 0; i < test_names.size(); i++) { |
+ batch.push_back(test_names[i]); |
+ |
+ // Use 0 to indicate unlimited batch size. |
+ if (batch.size() >= batch_limit_ && batch_limit_ != 0) { |
+ RunUnitTestsBatch(test_launcher, platform_delegate_, batch, launch_flags); |
+ batch.clear(); |
+ } |
+ } |
+ |
+ RunUnitTestsBatch(test_launcher, platform_delegate_, batch, launch_flags); |
+ |
+ return test_names.size(); |
+} |
+ |
+size_t UnitTestLauncherDelegate::RetryTests( |
+ TestLauncher* test_launcher, |
+ const std::vector<std::string>& test_names) { |
+ MessageLoop::current()->PostTask( |
+ FROM_HERE, |
+ Bind(&RunUnitTestsSerially, |
+ test_launcher, |
+ platform_delegate_, |
+ test_names, |
+ use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0)); |
+ return test_names.size(); |
+} |
+ |
} // namespace base |