summaryrefslogtreecommitdiffstats
path: root/chrome/test/test_launcher
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-10 16:43:27 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-10 16:43:27 +0000
commit42a116127704f97231765ff599cc1267747e2a70 (patch)
tree13f05cd5a09b75a2dd7649f12ddd0399b837beb4 /chrome/test/test_launcher
parent02528ba2437c46f8ced345431f8ee9ec25625577 (diff)
downloadchromium_src-42a116127704f97231765ff599cc1267747e2a70.zip
chromium_src-42a116127704f97231765ff599cc1267747e2a70.tar.gz
chromium_src-42a116127704f97231765ff599cc1267747e2a70.tar.bz2
This CL makes a more generic browser test launcher and a DLL of the
interactive UI tests, so the interactive UI tests can be run isolated. BUG=None TEST=Run the the interactive ui tests with the new launcher: test_launcher --lib=interactive_ui_tests_dll Review URL: http://codereview.chromium.org/197045 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25863 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/test/test_launcher')
-rw-r--r--chrome/test/test_launcher/in_proc_test_runner.cc135
-rw-r--r--chrome/test/test_launcher/out_of_proc_test_runner.cc86
-rw-r--r--chrome/test/test_launcher/run_all_unittests.cc46
-rw-r--r--chrome/test/test_launcher/test_runner.cc132
-rw-r--r--chrome/test/test_launcher/test_runner.h52
5 files changed, 451 insertions, 0 deletions
diff --git a/chrome/test/test_launcher/in_proc_test_runner.cc b/chrome/test/test_launcher/in_proc_test_runner.cc
new file mode 100644
index 0000000..379d4e5
--- /dev/null
+++ b/chrome/test/test_launcher/in_proc_test_runner.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2009 The Chromium 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 <string>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/native_library.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+
+#include "chrome/test/test_launcher/test_runner.h"
+
+// This version of the test launcher loads a dynamic library containing the
+// tests and executes the them in that library. When the test has been run the
+// library is unloaded, to ensure atexit handlers are run and static
+// initializers will be run again for the next test.
+
+namespace {
+
+const wchar_t* const kLibNameFlag = L"lib";
+const wchar_t* const kGTestListTestsFlag = L"gtest_list_tests";
+
+class InProcTestRunner : public tests::TestRunner {
+ public:
+ explicit InProcTestRunner(const std::wstring& lib_name)
+ : lib_name_(lib_name),
+ dynamic_lib_(NULL),
+ run_test_proc_(NULL) {
+ }
+
+ ~InProcTestRunner() {
+ if (!dynamic_lib_)
+ return;
+ base::UnloadNativeLibrary(dynamic_lib_);
+ LOG(INFO) << "Unloaded " << base::GetNativeLibraryName(lib_name_);
+ }
+
+ bool Init() {
+ FilePath lib_path;
+ CHECK(PathService::Get(base::FILE_EXE, &lib_path));
+ lib_path = lib_path.DirName().Append(base::GetNativeLibraryName(lib_name_));
+
+ LOG(INFO) << "Loading '" << lib_path.value() << "'";
+
+ dynamic_lib_ = base::LoadNativeLibrary(lib_path);
+ if (!dynamic_lib_) {
+ LOG(ERROR) << "Failed to load " << lib_path.value();
+ return false;
+ }
+
+ run_test_proc_ = reinterpret_cast<RunTestProc>(
+ base::GetFunctionPointerFromNativeLibrary(dynamic_lib_, "RunTests"));
+ if (!run_test_proc_) {
+ LOG(ERROR) <<
+ "Failed to find RunTest function in " << lib_path.value();
+ return false;
+ }
+
+ return true;
+ }
+
+ // Returns true if the test succeeded, false if it failed.
+ bool RunTest(const std::string& test_name) {
+ std::string filter_flag = StringPrintf("--gtest_filter=%s",
+ test_name.c_str());
+ char* argv[3];
+ argv[0] = const_cast<char*>("");
+ argv[1] = const_cast<char*>(filter_flag.c_str());
+ // Always enable disabled tests. This method is not called with disabled
+ // tests unless this flag was specified to the test launcher.
+ argv[2] = "--gtest_also_run_disabled_tests";
+ return RunAsIs(3, argv) == 0;
+ }
+
+ // Calls-in to GTest with the arguments we were started with.
+ int RunAsIs(int argc, char** argv) {
+ return (run_test_proc_)(argc, argv);
+ }
+
+ private:
+ typedef int (CDECL *RunTestProc)(int, char**);
+
+ std::wstring lib_name_;
+ base::NativeLibrary dynamic_lib_;
+ RunTestProc run_test_proc_;
+
+ DISALLOW_COPY_AND_ASSIGN(InProcTestRunner);
+};
+
+class InProcTestRunnerFactory : public tests::TestRunnerFactory {
+ public:
+ explicit InProcTestRunnerFactory(const std::wstring& lib_name)
+ : lib_name_(lib_name) {
+ }
+
+ virtual tests::TestRunner* CreateTestRunner() const {
+ return new InProcTestRunner(lib_name_);
+ }
+
+ private:
+ std::wstring lib_name_;
+
+ DISALLOW_COPY_AND_ASSIGN(InProcTestRunnerFactory);
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ base::AtExitManager at_exit_manager;
+
+ CommandLine::Init(argc, argv);
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+ std::wstring lib_name = command_line->GetSwitchValue(kLibNameFlag);
+ if (lib_name.empty()) {
+ LOG(ERROR) << "No dynamic library name specified. You must specify one with"
+ " the --lib=<lib_name> option.";
+ return 1;
+ }
+
+ if (command_line->HasSwitch(kGTestListTestsFlag)) {
+ InProcTestRunner test_runner(lib_name);
+ if (!test_runner.Init())
+ return 1;
+ return test_runner.RunAsIs(argc, argv);
+ }
+
+ InProcTestRunnerFactory test_runner_factory(lib_name);
+ return tests::RunTests(test_runner_factory) ? 0 : 1;
+}
diff --git a/chrome/test/test_launcher/out_of_proc_test_runner.cc b/chrome/test/test_launcher/out_of_proc_test_runner.cc
new file mode 100644
index 0000000..3d02bcb
--- /dev/null
+++ b/chrome/test/test_launcher/out_of_proc_test_runner.cc
@@ -0,0 +1,86 @@
+// Copyright (c) 2009 The Chromium 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 <string>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+
+#include "chrome/test/test_launcher/test_runner.h"
+#include "chrome/test/unit/chrome_test_suite.h"
+
+// This version of the test launcher forks a new process for each test it runs.
+
+namespace {
+
+const wchar_t* const kGTestListTestsFlag = L"gtest_list_tests";
+const wchar_t* const kChildProcessFlag = L"child";
+
+class OutOfProcTestRunner : public tests::TestRunner {
+ public:
+ OutOfProcTestRunner() {
+ }
+
+ virtual ~OutOfProcTestRunner() {
+ }
+
+ bool Init() {
+ return true;
+ }
+
+ // Returns true if the test succeeded, false if it failed.
+ bool RunTest(const std::string& test_name) {
+ const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
+ CommandLine new_cmd_line(cmd_line->argv());
+ // 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(L"gtest_also_run_disabled_tests");
+ new_cmd_line.AppendSwitchWithValue(L"gtest_filter", ASCIIToWide(test_name));
+ new_cmd_line.AppendSwitch(kChildProcessFlag);
+
+ base::ProcessHandle process_handle;
+ bool r = base::LaunchApp(new_cmd_line, false, false, &process_handle);
+ if (!r)
+ return false;
+
+ int exit_code = 0;
+ r = base::WaitForExitCode(process_handle, &exit_code);
+ if (!r)
+ return false;
+
+ return exit_code == 0;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OutOfProcTestRunner);
+};
+
+class OutOfProcTestRunnerFactory : public tests::TestRunnerFactory {
+ public:
+ OutOfProcTestRunnerFactory() { }
+
+ virtual tests::TestRunner* CreateTestRunner() const {
+ return new OutOfProcTestRunner();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OutOfProcTestRunnerFactory);
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+
+ if (command_line->HasSwitch(kChildProcessFlag))
+ return ChromeTestSuite(argc, argv).Run();
+
+ if (command_line->HasSwitch(kGTestListTestsFlag))
+ return ChromeTestSuite(argc, argv).Run();
+
+ OutOfProcTestRunnerFactory test_runner_factory;
+ return tests::RunTests(test_runner_factory) ? 0 : 1;
+}
diff --git a/chrome/test/test_launcher/run_all_unittests.cc b/chrome/test/test_launcher/run_all_unittests.cc
new file mode 100644
index 0000000..e4eb431
--- /dev/null
+++ b/chrome/test/test_launcher/run_all_unittests.cc
@@ -0,0 +1,46 @@
+// Copyright (c) 2009 The Chromium 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 "base/string_util.h"
+#include "chrome/test/unit/chrome_test_suite.h"
+
+#if defined(OS_WIN)
+#define DLLEXPORT __declspec(dllexport)
+#else
+#define DLLEXPORT
+#define CDECL
+#endif
+
+// We use extern C for the prototype DLLEXPORT to avoid C++ name mangling.
+extern "C" {
+DLLEXPORT int CDECL RunTests(int argc, char **argv) {
+ return ChromeTestSuite(argc, argv).Run();
+}
+}
+
+#if defined(OS_WIN)
+BOOL WINAPI DllMain(HINSTANCE dll_module, DWORD reason, LPVOID reserved) {
+ if (reason == DLL_PROCESS_DETACH) {
+ // The CRichEditCtrl (used by the omnibox) calls OleInitialize, but somehow
+ // does not always calls OleUninitialize, causing an unbalanced Ole
+ // initialization that triggers a DCHECK in ScopedOleInitializer the next
+ // time we run a test.
+ // This behavior has been seen on some Vista boxes, but not all of them.
+ // There is a flag to prevent Ole initialization in CRichEditCtrl (see
+ // http://support.microsoft.com/kb/238989), but it is set to 0 in recent
+ // Windows versions.
+ // This is a dirty hack to make sure the OleCount is back to 0 in all cases,
+ // so the next test will have Ole unitialized, as expected.
+
+ if (OleInitialize(NULL) == S_FALSE) {
+ // We were already initialized, balance that extra-initialization.
+ OleUninitialize();
+ }
+ // Balance the OleInitialize from the above test.
+ OleUninitialize();
+ }
+ return TRUE;
+}
+
+#endif
diff --git a/chrome/test/test_launcher/test_runner.cc b/chrome/test/test_launcher/test_runner.cc
new file mode 100644
index 0000000..4fb34cd
--- /dev/null
+++ b/chrome/test/test_launcher/test_runner.cc
@@ -0,0 +1,132 @@
+// Copyright (c) 2009 The Chromium 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 "chrome/test/test_launcher/test_runner.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+
+namespace {
+
+const wchar_t* const kGTestListTestsFlag = L"gtest_list_tests";
+const wchar_t* const kGTestRunDisabledTestsFlag =
+ L"gtest_also_run_disabled_tests";
+
+// Retrieves the list of tests to run by running gtest with the
+// --gtest_list_tests flag in a forked process and parsing its output.
+// |command_line| should contain the command line used to start the browser
+// test launcher, it is expected that it does not contain the
+// --gtest_list_tests flag already.
+// Note: we cannot implement this in-process for InProcessTestRunner as GTest
+// prints to the stdout and there are no good way of temporarily redirecting
+// outputs.
+bool GetTestList(const CommandLine& command_line,
+ std::vector<std::string>* test_list) {
+ DCHECK(!command_line.HasSwitch(kGTestListTestsFlag));
+
+ // Run ourselves with the --gtest_list_tests option and read the output.
+ CommandLine new_command_line(command_line);
+ new_command_line.AppendSwitch(kGTestListTestsFlag);
+ std::string output;
+ if (!base::GetAppOutput(new_command_line, &output))
+ return false;
+
+ // The output looks like:
+ // TestCase.
+ // Test1
+ // Test2
+ // OtherTestCase.
+ // FooTest
+ // ...
+ std::vector<std::string> lines;
+ SplitString(output, '\n', &lines);
+
+ std::string test_case;
+ for (std::vector<std::string>::const_iterator iter = lines.begin();
+ iter != lines.end(); ++iter) {
+ std::string line = *iter;
+ if (line.empty())
+ continue; // Just ignore empty lines if any.
+
+ if (line[line.size() - 1] == '.') {
+ // This is a new test case.
+ test_case = line;
+ continue;
+ }
+
+ if (!command_line.HasSwitch(kGTestRunDisabledTestsFlag) &&
+ line.find("DISABLED") != std::string::npos)
+ continue; // Skip disabled tests.
+
+ // We are dealing with a test.
+ test_list->push_back(test_case + line);
+ }
+ return true;
+}
+
+} // namespace
+
+namespace tests {
+
+TestRunner::TestRunner() {
+}
+
+TestRunner::~TestRunner() {
+}
+
+bool RunTests(const TestRunnerFactory& test_runner_factory) {
+ const CommandLine* command_line = CommandLine::ForCurrentProcess();
+
+ DCHECK(!command_line->HasSwitch(kGTestListTestsFlag));
+
+ // First let's get the list of tests we need to run.
+ std::vector<std::string> test_list;
+ if (!GetTestList(*command_line, &test_list)) {
+ printf("Failed to retrieve the tests to run.\n");
+ return false;
+ }
+
+ if (test_list.empty()) {
+ printf("No tests to run.\n");
+ return false;
+ }
+
+ int test_run_count = 0;
+ std::vector<std::string> failed_tests;
+ for (std::vector<std::string>::const_iterator iter = test_list.begin();
+ iter != test_list.end(); ++iter) {
+ std::string test_name = *iter;
+ scoped_ptr<TestRunner> test_runner(test_runner_factory.CreateTestRunner());
+ if (!test_runner.get() || !test_runner->Init())
+ return false;
+ test_run_count++;
+ if (!test_runner->RunTest(test_name.c_str())) {
+ if (std::find(failed_tests.begin(), failed_tests.end(), test_name) ==
+ failed_tests.end()) {
+ failed_tests.push_back(*iter);
+ }
+ }
+ }
+
+ printf("%d test%s run\n", test_run_count, test_run_count > 1 ? "s" : "");
+ printf("%d test%s failed\n", static_cast<int>(failed_tests.size()),
+ failed_tests.size() > 1 ? "s" : "");
+ if (failed_tests.empty())
+ return true;
+
+ printf("Failing tests:\n");
+ for (std::vector<std::string>::const_iterator iter = failed_tests.begin();
+ iter != failed_tests.end(); ++iter) {
+ printf("%s\n", iter->c_str());
+ }
+
+ return false;
+}
+
+} // namespace
diff --git a/chrome/test/test_launcher/test_runner.h b/chrome/test/test_launcher/test_runner.h
new file mode 100644
index 0000000..6700dde
--- /dev/null
+++ b/chrome/test/test_launcher/test_runner.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2009 The Chromium 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 CHROME_TEST_TEST_LAUNCHER_TEST_RUNNER_
+#define CHROME_TEST_TEST_LAUNCHER_TEST_RUNNER_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace tests {
+
+class TestRunnerFactory;
+
+// Runs the tests specified by the --gtest_filter flag specified in the command
+// line that started this process.
+// Returns true if all tests succeeded, false if there were no tests to run, or
+// one or more tests failed, or if initialization failed.
+// Results are printed to stdout.
+bool RunTests(const TestRunnerFactory& test_runner_factory);
+
+// This class defines a way to run tests in an isolated environment (each test
+// having its static variables uninitialized).
+// There are 2 implementations, in-process and out-of-process.
+class TestRunner {
+ public:
+ TestRunner();
+ virtual ~TestRunner();
+
+ // Called once before the TestRunner is used. Gives it an opportunity to
+ // perform any requried initialization. Should return true if the
+ // initialization was successful.
+ virtual bool Init() = 0;
+
+ // Runs the test named |test_name| and returns true if the test succeeded,
+ // false if it failed.
+ virtual bool RunTest(const std::string& test_name) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestRunner);
+};
+
+class TestRunnerFactory {
+ public:
+ virtual TestRunner* CreateTestRunner() const = 0;
+};
+
+} // namespace
+
+#endif // CHROME_TEST_TEST_LAUNCHER_TEST_RUNNER_