#include "base/bind.h"
#include "base/command_line.h"
#include "base/environment.h"
-#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/format_macros.h"
+#include "base/hash.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#endif
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
namespace base {
+// Launches a child process using |command_line|. If the child process is still
+// running after |timeout|, it is terminated and |*was_timeout| is set to true.
+// Returns exit code of the process.
+int LaunchChildTestProcessWithOptions(const CommandLine& command_line,
+ const LaunchOptions& options,
+ int flags,
+ base::TimeDelta timeout,
+ bool* was_timeout);
+
// See https://groups.google.com/a/chromium.org/d/msg/chromium-dev/nkdTP7sstSc/uT3FaE_sgkAJ .
using ::operator<<;
// debugging in case the process hangs anyway.
const int kOutputTimeoutSeconds = 15;
+// Limit of output snippet lines when printing to stdout.
+// Avoids flooding the logs with amount of output that gums up
+// the infrastructure.
+const size_t kOutputSnippetLinesLimit = 5000;
+
// Set of live launch test processes with corresponding lock (it is allowed
// for callers to launch processes on different threads).
LazyInstance<std::map<ProcessHandle, CommandLine> > g_live_processes
void DoLaunchChildTestProcess(
const CommandLine& command_line,
base::TimeDelta timeout,
+ int flags,
bool redirect_stdio,
scoped_refptr<MessageLoopProxy> message_loop_proxy,
const TestLauncher::LaunchChildGTestProcessCallback& callback) {
bool was_timeout = false;
int exit_code = LaunchChildTestProcessWithOptions(
- command_line, options, timeout, &was_timeout);
+ command_line, options, flags, timeout, &was_timeout);
if (redirect_stdio) {
#if defined(OS_WIN)
this,
&TestLauncher::OnOutputTimeout),
parallel_jobs_(parallel_jobs) {
- if (BotModeEnabled()) {
- fprintf(stdout,
- "Enabling defaults optimized for continuous integration bots.\n");
- fflush(stdout);
-
- // Enable test retries by default for bots. This can be still overridden
- // from command line using --test-launcher-retry-limit flag.
- retry_limit_ = 3;
- } else {
- // Default to serial test execution if not running on a bot. This makes it
- // possible to disable stdio redirection and can still be overridden with
- // --test-launcher-jobs flag.
- parallel_jobs_ = 1;
- }
}
TestLauncher::~TestLauncher() {
worker_pool_owner_->pool()->Shutdown();
}
-bool TestLauncher::Run(int argc, char** argv) {
+bool TestLauncher::Run() {
if (!Init())
return false;
const CommandLine& command_line,
const std::string& wrapper,
base::TimeDelta timeout,
+ int flags,
const LaunchChildGTestProcessCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
Bind(&DoLaunchChildTestProcess,
new_command_line,
timeout,
+ flags,
redirect_stdio,
MessageLoopProxy::current(),
Bind(&TestLauncher::OnLaunchTestProcessFinished,
<< ": " << print_test_stdio;
}
if (print_snippet) {
- fprintf(stdout, "%s", result.output_snippet.c_str());
+ std::vector<std::string> snippet_lines;
+ SplitString(result.output_snippet, '\n', &snippet_lines);
+ if (snippet_lines.size() > kOutputSnippetLinesLimit) {
+ size_t truncated_size = snippet_lines.size() - kOutputSnippetLinesLimit;
+ snippet_lines.erase(
+ snippet_lines.begin(),
+ snippet_lines.begin() + truncated_size);
+ snippet_lines.insert(snippet_lines.begin(), "<truncated>");
+ }
+ fprintf(stdout, "%s", JoinString(snippet_lines, "\n").c_str());
fflush(stdout);
}
tests_to_retry_.insert(result.full_name);
}
- if (result.status == TestResult::TEST_UNKNOWN)
- results_tracker_.AddGlobalTag(kUnreliableResultsTag);
-
results_tracker_.AddTestResult(result);
// TODO(phajdan.jr): Align counter (padding).
test_broken_count_++;
}
size_t broken_threshold =
- std::max(static_cast<size_t>(10), test_started_count_ / 10);
+ std::max(static_cast<size_t>(20), test_started_count_ / 10);
if (test_broken_count_ >= broken_threshold) {
fprintf(stdout, "Too many badly broken tests (%" PRIuS "), exiting now.\n",
test_broken_count_);
test_started_count_ += retry_started_count;
}
+// static
+std::string TestLauncher::FormatFullTestName(const std::string& test_case_name,
+ const std::string& test_name) {
+ return test_case_name + "." + test_name;
+}
+
bool TestLauncher::Init() {
const CommandLine* command_line = CommandLine::ForCurrentProcess();
}
retry_limit_ = retry_limit;
+ } else if (!CommandLine::ForCurrentProcess()->HasSwitch(kGTestFilterFlag)) {
+ // Retry failures 3 times by default if we are running all of the tests.
+ retry_limit_ = 3;
}
if (CommandLine::ForCurrentProcess()->HasSwitch(
}
parallel_jobs_ = jobs;
+ } else if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestFilterFlag)) {
+ // Do not run jobs in parallel by default if we are running a subset of
+ // the tests.
+ parallel_jobs_ = 1;
}
+
fprintf(stdout, "Using %" PRIuS " parallel jobs.\n", parallel_jobs_);
fflush(stdout);
worker_pool_owner_.reset(
void TestLauncher::RunTests() {
testing::UnitTest* const unit_test = testing::UnitTest::GetInstance();
- int num_runnable_tests = 0;
-
std::vector<std::string> test_names;
for (int i = 0; i < unit_test->total_test_case_count(); ++i) {
const testing::TestCase* test_case = unit_test->GetTestCase(i);
for (int j = 0; j < test_case->total_test_count(); ++j) {
const testing::TestInfo* test_info = test_case->GetTestInfo(j);
- std::string test_name = test_info->test_case_name();
- test_name.append(".");
- test_name.append(test_info->name());
+ std::string test_name = FormatFullTestName(
+ test_info->test_case_name(), test_info->name());
results_tracker_.AddTest(test_name);
continue;
}
+ if (!launcher_delegate_->ShouldRunTest(test_case, test_info))
+ continue;
+
// Skip the test that doesn't match the filter (if given).
if (!positive_test_filter_.empty()) {
bool found = false;
if (excluded)
continue;
- if (!launcher_delegate_->ShouldRunTest(test_case, test_info))
- continue;
-
- if (num_runnable_tests++ % total_shards_ != shard_index_)
+ if (base::Hash(test_name) % total_shards_ !=
+ static_cast<uint32>(shard_index_)) {
continue;
+ }
test_names.push_back(test_name);
}
}
void TestLauncher::OnTestIterationFinished() {
+ TestResultsTracker::TestStatusMap tests_by_status(
+ results_tracker_.GetTestStatusMapForCurrentIteration());
+ if (!tests_by_status[TestResult::TEST_UNKNOWN].empty())
+ results_tracker_.AddGlobalTag(kUnreliableResultsTag);
+
// When we retry tests, success is determined by having nothing more
// to retry (everything eventually passed), as opposed to having
// no failures at all.
return snippet;
}
-int LaunchChildGTestProcess(const CommandLine& command_line,
- const std::string& wrapper,
- base::TimeDelta timeout,
- bool* was_timeout) {
- LaunchOptions options;
-
-#if defined(OS_POSIX)
- // On POSIX, we launch the test in a new process group with pgid equal to
- // its pid. Any child processes that the test may create will inherit the
- // same pgid. This way, if the test is abruptly terminated, we can clean up
- // any orphaned child processes it may have left behind.
- options.new_process_group = true;
-#endif
-
- return LaunchChildTestProcessWithOptions(
- PrepareCommandLineForGTest(command_line, wrapper),
- options,
- timeout,
- was_timeout);
-}
-
CommandLine PrepareCommandLineForGTest(const CommandLine& command_line,
const std::string& wrapper) {
CommandLine new_command_line(command_line.GetProgram());
return new_command_line;
}
+// TODO(phajdan.jr): Move to anonymous namespace.
int LaunchChildTestProcessWithOptions(const CommandLine& command_line,
const LaunchOptions& options,
+ int flags,
base::TimeDelta timeout,
bool* was_timeout) {
#if defined(OS_POSIX)
#if defined(OS_WIN)
DCHECK(!new_options.job_handle);
- win::ScopedHandle job_handle(CreateJobObject(NULL, NULL));
- if (!job_handle.IsValid()) {
- LOG(ERROR) << "Could not create JobObject.";
- return -1;
- }
+ win::ScopedHandle job_handle;
+ if (flags & TestLauncher::USE_JOB_OBJECTS) {
+ job_handle.Set(CreateJobObject(NULL, NULL));
+ if (!job_handle.IsValid()) {
+ LOG(ERROR) << "Could not create JobObject.";
+ return -1;
+ }
- // Allow break-away from job since sandbox and few other places rely on it
- // on Windows versions prior to Windows 8 (which supports nested jobs).
- // TODO(phajdan.jr): Do not allow break-away on Windows 8.
- if (!SetJobObjectLimitFlags(job_handle.Get(),
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE |
- JOB_OBJECT_LIMIT_BREAKAWAY_OK)) {
- LOG(ERROR) << "Could not SetJobObjectLimitFlags.";
- return -1;
- }
+ DWORD job_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
- new_options.job_handle = job_handle.Get();
+ // Allow break-away from job since sandbox and few other places rely on it
+ // on Windows versions prior to Windows 8 (which supports nested jobs).
+ if (win::GetVersion() < win::VERSION_WIN8 &&
+ flags & TestLauncher::ALLOW_BREAKAWAY_FROM_JOB) {
+ job_flags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK;
+ }
+
+ if (!SetJobObjectLimitFlags(job_handle.Get(), job_flags)) {
+ LOG(ERROR) << "Could not SetJobObjectLimitFlags.";
+ return -1;
+ }
+
+ new_options.job_handle = job_handle.Get();
+ }
#endif // defined(OS_WIN)
+#if defined(OS_LINUX)
+ // To prevent accidental privilege sharing to an untrusted child, processes
+ // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this
+ // new child will be privileged and trusted.
+ new_options.allow_new_privs = true;
+#endif
+
base::ProcessHandle process_handle;
{