Index: content/public/test/test_launcher.cc |
diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc |
index f17dbdf2fda1feb12e8721389b0bbc1d2c2539bd..0478645826f16bf1a4c95006c3316540886ac077 100644 |
--- a/content/public/test/test_launcher.cc |
+++ b/content/public/test/test_launcher.cc |
@@ -16,11 +16,14 @@ |
#include "base/memory/linked_ptr.h" |
#include "base/memory/scoped_ptr.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/stl_util.h" |
#include "base/strings/string_number_conversions.h" |
#include "base/strings/string_util.h" |
#include "base/strings/utf_string_conversions.h" |
+#include "base/test/parallel_test_launcher.h" |
#include "base/test/test_launcher.h" |
#include "base/test/test_suite.h" |
+#include "base/test/test_switches.h" |
#include "base/test/test_timeouts.h" |
#include "base/time/time.h" |
#include "content/public/app/content_main.h" |
@@ -57,99 +60,11 @@ const char kPreTestPrefix[] = "PRE_"; |
const char kManualTestPrefix[] = "MANUAL_"; |
TestLauncherDelegate* g_launcher_delegate; |
-} |
- |
-namespace { |
- |
-int DoRunTestInternal(const testing::TestCase* test_case, |
- const std::string& test_name, |
- const CommandLine& command_line, |
- base::TimeDelta default_timeout, |
- bool* was_timeout) { |
- if (test_case) { |
- std::string pre_test_name = test_name; |
- std::string replace_string = std::string(".") + kPreTestPrefix; |
- ReplaceFirstSubstringAfterOffset(&pre_test_name, 0, ".", replace_string); |
- for (int i = 0; i < test_case->total_test_count(); ++i) { |
- const testing::TestInfo* test_info = test_case->GetTestInfo(i); |
- std::string cur_test_name = test_info->test_case_name(); |
- cur_test_name.append("."); |
- cur_test_name.append(test_info->name()); |
- if (cur_test_name == pre_test_name) { |
- int exit_code = DoRunTestInternal(test_case, |
- pre_test_name, |
- command_line, |
- default_timeout, |
- was_timeout); |
- if (exit_code != 0) |
- return exit_code; |
- } |
- } |
- } |
- |
- CommandLine new_cmd_line(command_line.GetProgram()); |
- CommandLine::SwitchMap switches = command_line.GetSwitches(); |
- |
- // Strip out gtest_output flag because otherwise we would overwrite results |
- // of the other tests. |
- switches.erase(base::kGTestOutputFlag); |
- |
- for (CommandLine::SwitchMap::const_iterator iter = switches.begin(); |
- iter != switches.end(); ++iter) { |
- new_cmd_line.AppendSwitchNative(iter->first, iter->second); |
- } |
- |
- // Always enable disabled tests. This method is not called with disabled |
- // tests unless this flag was specified to the browser test executable. |
- new_cmd_line.AppendSwitch("gtest_also_run_disabled_tests"); |
- new_cmd_line.AppendSwitchASCII("gtest_filter", test_name); |
- new_cmd_line.AppendSwitch(kSingleProcessTestsFlag); |
- |
- char* browser_wrapper = getenv("BROWSER_WRAPPER"); |
- int exit_code = base::LaunchChildGTestProcess( |
- new_cmd_line, |
- browser_wrapper ? browser_wrapper : std::string(), |
- default_timeout, |
- was_timeout); |
- if (*was_timeout) { |
- LOG(ERROR) << "Test timeout (" << default_timeout.InMilliseconds() |
- << " ms) exceeded for " << test_name; |
- } |
- |
- return exit_code; |
-} |
- |
-// Runs test specified by |test_name| in a child process, |
-// and returns the exit code. |
-int DoRunTest(TestLauncherDelegate* launcher_delegate, |
- const testing::TestCase* test_case, |
- const std::string& test_name, |
- base::TimeDelta default_timeout, |
- bool* was_timeout) { |
- if (was_timeout) |
- *was_timeout = false; |
- |
-#if defined(OS_MACOSX) |
- // Some of the below method calls will leak objects if there is no |
- // autorelease pool in place. |
- base::mac::ScopedNSAutoreleasePool pool; |
-#endif |
- |
- base::ScopedTempDir temp_dir; |
- // Create a new data dir and pass it to the child. |
- if (!temp_dir.CreateUniqueTempDir() || !temp_dir.IsValid()) { |
- LOG(ERROR) << "Error creating temp data directory"; |
- return -1; |
- } |
- |
- CommandLine new_cmd_line(*CommandLine::ForCurrentProcess()); |
- if (!launcher_delegate->AdjustChildProcessCommandLine(&new_cmd_line, |
- temp_dir.path())) { |
- return -1; |
- } |
- return DoRunTestInternal( |
- test_case, test_name, new_cmd_line, default_timeout, was_timeout); |
+std::string RemoveAnyPrePrefixes(const std::string& test_name) { |
+ std::string result(test_name); |
+ ReplaceSubstringsAfterOffset(&result, 0, kPreTestPrefix, std::string()); |
+ return result; |
} |
void PrintUsage() { |
@@ -171,14 +86,19 @@ void PrintUsage() { |
// wrapping a lower-level test launcher with content-specific code. |
class WrapperTestLauncherDelegate : public base::TestLauncherDelegate { |
public: |
- explicit WrapperTestLauncherDelegate( |
- content::TestLauncherDelegate* launcher_delegate) |
+ WrapperTestLauncherDelegate(content::TestLauncherDelegate* launcher_delegate, |
+ size_t jobs) |
: launcher_delegate_(launcher_delegate), |
timeout_count_(0), |
- printed_timeout_message_(false) { |
+ printed_timeout_message_(false), |
+ parallel_launcher_(jobs) { |
+ CHECK(temp_dir_.CreateUniqueTempDir()); |
} |
// base::TestLauncherDelegate: |
+ virtual std::string GetTestNameForFiltering( |
+ const testing::TestCase* test_case, |
+ const testing::TestInfo* test_info) OVERRIDE; |
virtual bool ShouldRunTest(const testing::TestCase* test_case, |
const testing::TestInfo* test_info) OVERRIDE; |
virtual void RunTest( |
@@ -188,6 +108,25 @@ class WrapperTestLauncherDelegate : public base::TestLauncherDelegate { |
virtual void RunRemainingTests() OVERRIDE; |
private: |
+ struct TestInfo { |
+ std::string test_case_name; |
+ std::string test_name; |
+ base::TestLauncherDelegate::TestResultCallback callback; |
+ }; |
+ |
+ friend bool CompareTestInfo(const TestInfo& a, const TestInfo& b); |
+ |
+ // Launches test from |test_info| using |command_line| and parallel launcher. |
+ void DoRunTest(const TestInfo& test_info, const CommandLine& command_line); |
+ |
+ // Callback to receive result of a test. |
+ void GTestCallback( |
+ const TestInfo& test_info, |
+ int exit_code, |
+ const base::TimeDelta& elapsed_time, |
+ bool was_timeout, |
+ const std::string& output); |
+ |
content::TestLauncherDelegate* launcher_delegate_; |
// Number of times a test timeout occurred. |
@@ -197,18 +136,28 @@ class WrapperTestLauncherDelegate : public base::TestLauncherDelegate { |
// to avoid doing it more than once. |
bool printed_timeout_message_; |
+ base::ParallelTestLauncher parallel_launcher_; |
+ |
+ // Store all tests to run before running any of them to properly |
+ // handle PRE_ tests. |
+ std::vector<TestInfo> tests_to_run_; |
+ |
+ // Temporary directory for user data directories. |
+ base::ScopedTempDir temp_dir_; |
+ |
DISALLOW_COPY_AND_ASSIGN(WrapperTestLauncherDelegate); |
}; |
-bool WrapperTestLauncherDelegate::ShouldRunTest( |
+std::string WrapperTestLauncherDelegate::GetTestNameForFiltering( |
const testing::TestCase* test_case, |
const testing::TestInfo* test_info) { |
- std::string test_name = |
- std::string(test_case->name()) + "." + test_info->name(); |
- |
- if (StartsWithASCII(test_info->name(), kPreTestPrefix, true)) |
- return false; |
+ return RemoveAnyPrePrefixes( |
+ std::string(test_case->name()) + "." + test_info->name()); |
+} |
+bool WrapperTestLauncherDelegate::ShouldRunTest( |
+ const testing::TestCase* test_case, |
+ const testing::TestInfo* test_info) { |
if (StartsWithASCII(test_info->name(), kManualTestPrefix, true) && |
!CommandLine::ForCurrentProcess()->HasSwitch(kRunManualTestsFlag)) { |
return false; |
@@ -230,21 +179,114 @@ void WrapperTestLauncherDelegate::RunTest( |
const testing::TestCase* test_case, |
const testing::TestInfo* test_info, |
const base::TestLauncherDelegate::TestResultCallback& callback) { |
- base::TimeTicks start_time = base::TimeTicks::Now(); |
- bool was_timeout = false; |
- std::string test_name = |
- std::string(test_case->name()) + "." + test_info->name(); |
- int exit_code = DoRunTest(launcher_delegate_, |
- test_case, |
- test_name, |
- TestTimeouts::action_max_timeout(), |
- &was_timeout); |
- if (was_timeout) |
- timeout_count_++; |
+ TestInfo run_test_info; |
+ run_test_info.test_case_name = test_case->name(); |
+ run_test_info.test_name = test_info->name(); |
+ run_test_info.callback = callback; |
+ tests_to_run_.push_back(run_test_info); |
+} |
+ |
+bool CompareTestInfo(const WrapperTestLauncherDelegate::TestInfo& a, |
+ const WrapperTestLauncherDelegate::TestInfo& b) { |
+ if (a.test_case_name == b.test_case_name) { |
+ // Put PRE_ tests before tests that depend on them (e.g. PRE_Foo before Foo, |
+ // and PRE_PRE_Foo before PRE_Foo). |
+ if (std::string(kPreTestPrefix) + a.test_name == b.test_name) |
loislo
2013/09/26 05:47:33
This comparer doesn't work properly. It doesn't Tr
jam
2013/09/26 21:36:03
Pawel: ping
can this cause PRE tests to run in the
Paweł Hajdan Jr.
2013/09/26 23:32:28
Given this comparator is not StrictWeakOrdering, a
|
+ return false; |
+ if (a.test_name == std::string(kPreTestPrefix) + b.test_name) |
+ return true; |
+ } |
+ |
+ // Otherwise sort by full names, disregarding PRE_ completely so that |
+ // this can still be Strict Weak Ordering. |
+ std::string a_full( |
+ RemoveAnyPrePrefixes(a.test_case_name + "." + a.test_name)); |
+ std::string b_full( |
+ RemoveAnyPrePrefixes(b.test_case_name + "." + b.test_name)); |
+ |
+ return a_full < b_full; |
+} |
+ |
+void WrapperTestLauncherDelegate::RunRemainingTests() { |
+ std::sort(tests_to_run_.begin(), tests_to_run_.end(), CompareTestInfo); |
+ |
+ // PRE_ tests and tests that depend on them must share the same |
+ // data directory. Using test name as directory name leads to too long |
+ // names (exceeding UNIX_PATH_MAX, which creates a problem with |
+ // process_singleton_linux). Create a randomly-named temporary directory |
+ // and keep track of the names so that PRE_ tests can still re-use them. |
+ std::map<std::string, base::FilePath> temp_directories; |
+ |
+ for (size_t i = 0; i < tests_to_run_.size(); i++) { |
+ TestInfo test_info(tests_to_run_[i]); |
+ |
+ // Make sure PRE_ tests and tests that depend on them share the same |
+ // data directory - based it on the test name without prefixes. |
+ std::string test_name_no_pre = RemoveAnyPrePrefixes( |
+ test_info.test_case_name + "." + test_info.test_name); |
+ if (!ContainsKey(temp_directories, test_name_no_pre)) { |
+ base::FilePath temp_dir; |
+ CHECK(file_util::CreateTemporaryDirInDir( |
+ temp_dir_.path(), FILE_PATH_LITERAL("d"), &temp_dir)); |
+ temp_directories[test_name_no_pre] = temp_dir; |
+ } |
+ |
+ CommandLine new_cmd_line(*CommandLine::ForCurrentProcess()); |
+ CHECK(launcher_delegate_->AdjustChildProcessCommandLine( |
+ &new_cmd_line, temp_directories[test_name_no_pre])); |
+ |
+ DoRunTest(test_info, new_cmd_line); |
+ } |
+} |
+ |
+void WrapperTestLauncherDelegate::DoRunTest(const TestInfo& test_info, |
+ const CommandLine& command_line) { |
+ CommandLine new_cmd_line(command_line.GetProgram()); |
+ CommandLine::SwitchMap switches = command_line.GetSwitches(); |
+ |
+ // Strip out gtest_output flag because otherwise we would overwrite results |
+ // of the other tests. |
+ switches.erase(base::kGTestOutputFlag); |
+ |
+ for (CommandLine::SwitchMap::const_iterator iter = switches.begin(); |
+ iter != switches.end(); ++iter) { |
+ new_cmd_line.AppendSwitchNative(iter->first, iter->second); |
+ } |
+ |
+ // Always enable disabled tests. This method is not called with disabled |
+ // tests unless this flag was specified to the browser test executable. |
+ new_cmd_line.AppendSwitch("gtest_also_run_disabled_tests"); |
+ new_cmd_line.AppendSwitchASCII( |
+ "gtest_filter", |
+ test_info.test_case_name + "." + test_info.test_name); |
+ new_cmd_line.AppendSwitch(kSingleProcessTestsFlag); |
+ |
+ char* browser_wrapper = getenv("BROWSER_WRAPPER"); |
+ |
+ // PRE_ tests and tests that depend on them should share the sequence token |
+ // name, so that they are run serially. |
+ std::string test_name_no_pre = RemoveAnyPrePrefixes( |
+ test_info.test_case_name + "." + test_info.test_name); |
+ parallel_launcher_.LaunchNamedSequencedChildGTestProcess( |
+ test_name_no_pre, |
+ new_cmd_line, |
+ browser_wrapper ? browser_wrapper : std::string(), |
+ TestTimeouts::action_max_timeout(), |
+ base::Bind(&WrapperTestLauncherDelegate::GTestCallback, |
+ base::Unretained(this), |
+ test_info)); |
+} |
+ |
+void WrapperTestLauncherDelegate::GTestCallback( |
+ const TestInfo& test_info, |
+ int exit_code, |
+ const base::TimeDelta& elapsed_time, |
+ bool was_timeout, |
+ const std::string& output) { |
base::TestResult result; |
- result.test_case_name = test_case->name(); |
- result.test_name = test_info->name(); |
+ result.test_case_name = test_info.test_case_name; |
+ result.test_name = test_info.test_name; |
// TODO(phajdan.jr): Recognize crashes. |
if (exit_code == 0) |
@@ -254,13 +296,29 @@ void WrapperTestLauncherDelegate::RunTest( |
else |
result.status = base::TestResult::TEST_FAILURE; |
- result.elapsed_time = (base::TimeTicks::Now() - start_time); |
+ result.elapsed_time = elapsed_time; |
+ |
+ // TODO(phajdan.jr): Use base::PrintTestOutputSnippetOnFailure after migrating |
+ // away from run_test_cases.py (http://crbug.com/236893). |
+ fprintf(stdout, "%s", output.c_str()); |
+ fflush(stdout); |
- callback.Run(result); |
+ test_info.callback.Run(result); |
+ parallel_launcher_.ResetOutputWatchdog(); |
} |
-void WrapperTestLauncherDelegate::RunRemainingTests() { |
- // No need to do anything else here, we launch tests synchronously. |
+bool GetSwitchValueAsInt(const std::string& switch_name, int* result) { |
+ if (!CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) |
+ return true; |
+ |
+ std::string switch_value = |
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name); |
+ if (!base::StringToInt(switch_value, result) || *result < 1) { |
+ LOG(ERROR) << "Invalid value for " << switch_name << ": " << switch_value; |
+ return false; |
+ } |
+ |
+ return true; |
} |
} // namespace |
@@ -357,9 +415,13 @@ int LaunchTests(TestLauncherDelegate* launcher_delegate, |
testing::InitGoogleTest(&argc, argv); |
TestTimeouts::Initialize(); |
+ int jobs = 1; // TODO(phajdan.jr): Default to half the number of CPU cores. |
+ if (!GetSwitchValueAsInt(switches::kTestLauncherJobs, &jobs)) |
+ return 1; |
+ |
base::MessageLoopForIO message_loop; |
- WrapperTestLauncherDelegate delegate(launcher_delegate); |
+ WrapperTestLauncherDelegate delegate(launcher_delegate, jobs); |
return base::LaunchTests(&delegate, argc, argv); |
} |