| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/test/launcher/test_launcher.h" | 5 #include "base/test/launcher/test_launcher.h" |
| 6 | 6 |
| 7 #if defined(OS_POSIX) | 7 #if defined(OS_POSIX) |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #endif | 9 #endif |
| 10 | 10 |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 bool UnsetEnvironmentVariableIfExists(const std::string& name) { | 171 bool UnsetEnvironmentVariableIfExists(const std::string& name) { |
| 172 scoped_ptr<Environment> env(Environment::Create()); | 172 scoped_ptr<Environment> env(Environment::Create()); |
| 173 std::string str_val; | 173 std::string str_val; |
| 174 | 174 |
| 175 if (!env->GetVar(name.c_str(), &str_val)) | 175 if (!env->GetVar(name.c_str(), &str_val)) |
| 176 return true; | 176 return true; |
| 177 | 177 |
| 178 return env->UnSetVar(name.c_str()); | 178 return env->UnSetVar(name.c_str()); |
| 179 } | 179 } |
| 180 | 180 |
| 181 // Returns true if bot mode has been requested, i.e. defaults optimized |
| 182 // for continuous integration bots. This way developers don't have to remember |
| 183 // special command-line flags. |
| 184 bool BotModeEnabled() { |
| 185 scoped_ptr<Environment> env(Environment::Create()); |
| 186 return CommandLine::ForCurrentProcess()->HasSwitch( |
| 187 switches::kTestLauncherBotMode) || |
| 188 env->HasVar("CHROMIUM_TEST_LAUNCHER_BOT_MODE"); |
| 189 } |
| 190 |
| 181 // For a basic pattern matching for gtest_filter options. (Copied from | 191 // For a basic pattern matching for gtest_filter options. (Copied from |
| 182 // gtest.cc, see the comment below and http://crbug.com/44497) | 192 // gtest.cc, see the comment below and http://crbug.com/44497) |
| 183 bool PatternMatchesString(const char* pattern, const char* str) { | 193 bool PatternMatchesString(const char* pattern, const char* str) { |
| 184 switch (*pattern) { | 194 switch (*pattern) { |
| 185 case '\0': | 195 case '\0': |
| 186 case ':': // Either ':' or '\0' marks the end of the pattern. | 196 case ':': // Either ':' or '\0' marks the end of the pattern. |
| 187 return *str == '\0'; | 197 return *str == '\0'; |
| 188 case '?': // Matches any single character. | 198 case '?': // Matches any single character. |
| 189 return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); | 199 return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); |
| 190 case '*': // Matches any string (possibly empty) of characters. | 200 case '*': // Matches any string (possibly empty) of characters. |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 224 int exit_code, | 234 int exit_code, |
| 225 const TimeDelta& elapsed_time, | 235 const TimeDelta& elapsed_time, |
| 226 bool was_timeout, | 236 bool was_timeout, |
| 227 const std::string& output) { | 237 const std::string& output) { |
| 228 callback.Run(exit_code, elapsed_time, was_timeout, output); | 238 callback.Run(exit_code, elapsed_time, was_timeout, output); |
| 229 } | 239 } |
| 230 | 240 |
| 231 void DoLaunchChildTestProcess( | 241 void DoLaunchChildTestProcess( |
| 232 const CommandLine& command_line, | 242 const CommandLine& command_line, |
| 233 base::TimeDelta timeout, | 243 base::TimeDelta timeout, |
| 244 bool redirect_stdio, |
| 234 scoped_refptr<MessageLoopProxy> message_loop_proxy, | 245 scoped_refptr<MessageLoopProxy> message_loop_proxy, |
| 235 const TestLauncher::LaunchChildGTestProcessCallback& callback) { | 246 const TestLauncher::LaunchChildGTestProcessCallback& callback) { |
| 236 TimeTicks start_time = TimeTicks::Now(); | 247 TimeTicks start_time = TimeTicks::Now(); |
| 237 | 248 |
| 238 // Redirect child process output to a file. | 249 // Redirect child process output to a file. |
| 239 base::FilePath output_file; | 250 base::FilePath output_file; |
| 240 CHECK(file_util::CreateTemporaryFile(&output_file)); | 251 CHECK(file_util::CreateTemporaryFile(&output_file)); |
| 241 | 252 |
| 242 LaunchOptions options; | 253 LaunchOptions options; |
| 243 #if defined(OS_WIN) | 254 #if defined(OS_WIN) |
| 244 win::ScopedHandle handle; | 255 win::ScopedHandle handle; |
| 245 | 256 |
| 246 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 257 if (redirect_stdio) { |
| 247 switches::kTestLauncherDeveloperMode)) { | |
| 248 // Make the file handle inheritable by the child. | 258 // Make the file handle inheritable by the child. |
| 249 SECURITY_ATTRIBUTES sa_attr; | 259 SECURITY_ATTRIBUTES sa_attr; |
| 250 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); | 260 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); |
| 251 sa_attr.lpSecurityDescriptor = NULL; | 261 sa_attr.lpSecurityDescriptor = NULL; |
| 252 sa_attr.bInheritHandle = TRUE; | 262 sa_attr.bInheritHandle = TRUE; |
| 253 | 263 |
| 254 handle.Set(CreateFile(output_file.value().c_str(), | 264 handle.Set(CreateFile(output_file.value().c_str(), |
| 255 GENERIC_WRITE, | 265 GENERIC_WRITE, |
| 256 FILE_SHARE_READ | FILE_SHARE_DELETE, | 266 FILE_SHARE_READ | FILE_SHARE_DELETE, |
| 257 &sa_attr, | 267 &sa_attr, |
| 258 OPEN_EXISTING, | 268 OPEN_EXISTING, |
| 259 FILE_ATTRIBUTE_TEMPORARY, | 269 FILE_ATTRIBUTE_TEMPORARY, |
| 260 NULL)); | 270 NULL)); |
| 261 CHECK(handle.IsValid()); | 271 CHECK(handle.IsValid()); |
| 262 options.inherit_handles = true; | 272 options.inherit_handles = true; |
| 263 options.stdin_handle = INVALID_HANDLE_VALUE; | 273 options.stdin_handle = INVALID_HANDLE_VALUE; |
| 264 options.stdout_handle = handle.Get(); | 274 options.stdout_handle = handle.Get(); |
| 265 options.stderr_handle = handle.Get(); | 275 options.stderr_handle = handle.Get(); |
| 266 } | 276 } |
| 267 #elif defined(OS_POSIX) | 277 #elif defined(OS_POSIX) |
| 268 options.new_process_group = true; | 278 options.new_process_group = true; |
| 269 | 279 |
| 270 base::FileHandleMappingVector fds_mapping; | 280 base::FileHandleMappingVector fds_mapping; |
| 271 file_util::ScopedFD output_file_fd_closer; | 281 file_util::ScopedFD output_file_fd_closer; |
| 272 | 282 |
| 273 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 283 if (redirect_stdio) { |
| 274 switches::kTestLauncherDeveloperMode)) { | |
| 275 int output_file_fd = open(output_file.value().c_str(), O_RDWR); | 284 int output_file_fd = open(output_file.value().c_str(), O_RDWR); |
| 276 CHECK_GE(output_file_fd, 0); | 285 CHECK_GE(output_file_fd, 0); |
| 277 | 286 |
| 278 output_file_fd_closer.reset(&output_file_fd); | 287 output_file_fd_closer.reset(&output_file_fd); |
| 279 | 288 |
| 280 fds_mapping.push_back(std::make_pair(output_file_fd, STDOUT_FILENO)); | 289 fds_mapping.push_back(std::make_pair(output_file_fd, STDOUT_FILENO)); |
| 281 fds_mapping.push_back(std::make_pair(output_file_fd, STDERR_FILENO)); | 290 fds_mapping.push_back(std::make_pair(output_file_fd, STDERR_FILENO)); |
| 282 options.fds_to_remap = &fds_mapping; | 291 options.fds_to_remap = &fds_mapping; |
| 283 } | 292 } |
| 284 #endif | 293 #endif |
| 285 | 294 |
| 286 bool was_timeout = false; | 295 bool was_timeout = false; |
| 287 int exit_code = LaunchChildTestProcessWithOptions( | 296 int exit_code = LaunchChildTestProcessWithOptions( |
| 288 command_line, options, timeout, &was_timeout); | 297 command_line, options, timeout, &was_timeout); |
| 289 | 298 |
| 290 if (!CommandLine::ForCurrentProcess()->HasSwitch( | 299 if (redirect_stdio) { |
| 291 switches::kTestLauncherDeveloperMode)) { | |
| 292 #if defined(OS_WIN) | 300 #if defined(OS_WIN) |
| 293 FlushFileBuffers(handle.Get()); | 301 FlushFileBuffers(handle.Get()); |
| 294 handle.Close(); | 302 handle.Close(); |
| 295 #elif defined(OS_POSIX) | 303 #elif defined(OS_POSIX) |
| 296 output_file_fd_closer.reset(); | 304 output_file_fd_closer.reset(); |
| 297 #endif | 305 #endif |
| 298 } | 306 } |
| 299 | 307 |
| 300 std::string output_file_contents; | 308 std::string output_file_contents; |
| 301 CHECK(base::ReadFileToString(output_file, &output_file_contents)); | 309 CHECK(base::ReadFileToString(output_file, &output_file_contents)); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 size_t parallel_jobs) | 341 size_t parallel_jobs) |
| 334 : launcher_delegate_(launcher_delegate), | 342 : launcher_delegate_(launcher_delegate), |
| 335 total_shards_(1), | 343 total_shards_(1), |
| 336 shard_index_(0), | 344 shard_index_(0), |
| 337 cycles_(1), | 345 cycles_(1), |
| 338 test_started_count_(0), | 346 test_started_count_(0), |
| 339 test_finished_count_(0), | 347 test_finished_count_(0), |
| 340 test_success_count_(0), | 348 test_success_count_(0), |
| 341 test_broken_count_(0), | 349 test_broken_count_(0), |
| 342 retry_count_(0), | 350 retry_count_(0), |
| 343 retry_limit_(3), | 351 retry_limit_(0), |
| 344 run_result_(true), | 352 run_result_(true), |
| 345 watchdog_timer_(FROM_HERE, | 353 watchdog_timer_(FROM_HERE, |
| 346 TimeDelta::FromSeconds(kOutputTimeoutSeconds), | 354 TimeDelta::FromSeconds(kOutputTimeoutSeconds), |
| 347 this, | 355 this, |
| 348 &TestLauncher::OnOutputTimeout) { | 356 &TestLauncher::OnOutputTimeout), |
| 349 if (CommandLine::ForCurrentProcess()->HasSwitch( | 357 parallel_jobs_(parallel_jobs) { |
| 350 switches::kTestLauncherDeveloperMode)) { | 358 if (BotModeEnabled()) { |
| 351 parallel_jobs = 1; | |
| 352 retry_limit_ = 0; | |
| 353 fprintf(stdout, | 359 fprintf(stdout, |
| 354 "Forcing serial test execution in developer mode.\n" | 360 "Enabling defaults optimized for continuous integration bots.\n"); |
| 355 "Disabling test retries in developer mode.\n"); | |
| 356 fflush(stdout); | 361 fflush(stdout); |
| 362 |
| 363 // Enable test retries by default for bots. This can be still overridden |
| 364 // from command line using --test-launcher-retry-limit flag. |
| 365 retry_limit_ = 3; |
| 366 } else { |
| 367 // Default to serial test execution if not running on a bot. This makes it |
| 368 // possible to disable stdio redirection and can still be overridden with |
| 369 // --test-launcher-jobs flag. |
| 370 parallel_jobs_ = 1; |
| 357 } | 371 } |
| 358 worker_pool_owner_.reset( | |
| 359 new SequencedWorkerPoolOwner(parallel_jobs, "test_launcher")); | |
| 360 } | 372 } |
| 361 | 373 |
| 362 TestLauncher::~TestLauncher() { | 374 TestLauncher::~TestLauncher() { |
| 363 worker_pool_owner_->pool()->Shutdown(); | 375 if (worker_pool_owner_) |
| 376 worker_pool_owner_->pool()->Shutdown(); |
| 364 } | 377 } |
| 365 | 378 |
| 366 bool TestLauncher::Run(int argc, char** argv) { | 379 bool TestLauncher::Run(int argc, char** argv) { |
| 367 if (!Init()) | 380 if (!Init()) |
| 368 return false; | 381 return false; |
| 369 | 382 |
| 370 #if defined(OS_POSIX) | 383 #if defined(OS_POSIX) |
| 371 CHECK_EQ(0, pipe(g_shutdown_pipe)); | 384 CHECK_EQ(0, pipe(g_shutdown_pipe)); |
| 372 | 385 |
| 373 struct sigaction action; | 386 struct sigaction action; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 411 const CommandLine& command_line, | 424 const CommandLine& command_line, |
| 412 const std::string& wrapper, | 425 const std::string& wrapper, |
| 413 base::TimeDelta timeout, | 426 base::TimeDelta timeout, |
| 414 const LaunchChildGTestProcessCallback& callback) { | 427 const LaunchChildGTestProcessCallback& callback) { |
| 415 DCHECK(thread_checker_.CalledOnValidThread()); | 428 DCHECK(thread_checker_.CalledOnValidThread()); |
| 416 | 429 |
| 417 // Record the exact command line used to launch the child. | 430 // Record the exact command line used to launch the child. |
| 418 CommandLine new_command_line( | 431 CommandLine new_command_line( |
| 419 PrepareCommandLineForGTest(command_line, wrapper)); | 432 PrepareCommandLineForGTest(command_line, wrapper)); |
| 420 | 433 |
| 434 // When running in parallel mode we need to redirect stdio to avoid mixed-up |
| 435 // output. We also always redirect on the bots to get the test output into |
| 436 // JSON summary. |
| 437 bool redirect_stdio = (parallel_jobs_ > 1) || BotModeEnabled(); |
| 438 |
| 421 worker_pool_owner_->pool()->PostWorkerTask( | 439 worker_pool_owner_->pool()->PostWorkerTask( |
| 422 FROM_HERE, | 440 FROM_HERE, |
| 423 Bind(&DoLaunchChildTestProcess, | 441 Bind(&DoLaunchChildTestProcess, |
| 424 new_command_line, | 442 new_command_line, |
| 425 timeout, | 443 timeout, |
| 444 redirect_stdio, |
| 426 MessageLoopProxy::current(), | 445 MessageLoopProxy::current(), |
| 427 Bind(&TestLauncher::OnLaunchTestProcessFinished, | 446 Bind(&TestLauncher::OnLaunchTestProcessFinished, |
| 428 Unretained(this), | 447 Unretained(this), |
| 429 callback))); | 448 callback))); |
| 430 } | 449 } |
| 431 | 450 |
| 432 void TestLauncher::OnTestFinished(const TestResult& result) { | 451 void TestLauncher::OnTestFinished(const TestResult& result) { |
| 433 ++test_finished_count_; | 452 ++test_finished_count_; |
| 434 | 453 |
| 435 bool print_snippet = false; | 454 bool print_snippet = false; |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 646 if (!StringToInt(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 665 if (!StringToInt(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 647 switches::kTestLauncherRetryLimit), &retry_limit) || | 666 switches::kTestLauncherRetryLimit), &retry_limit) || |
| 648 retry_limit < 0) { | 667 retry_limit < 0) { |
| 649 LOG(ERROR) << "Invalid value for " << switches::kTestLauncherRetryLimit; | 668 LOG(ERROR) << "Invalid value for " << switches::kTestLauncherRetryLimit; |
| 650 return false; | 669 return false; |
| 651 } | 670 } |
| 652 | 671 |
| 653 retry_limit_ = retry_limit; | 672 retry_limit_ = retry_limit; |
| 654 } | 673 } |
| 655 | 674 |
| 675 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 676 switches::kTestLauncherJobs)) { |
| 677 int jobs = -1; |
| 678 if (!StringToInt(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 679 switches::kTestLauncherJobs), &jobs) || |
| 680 jobs < 0) { |
| 681 LOG(ERROR) << "Invalid value for " << switches::kTestLauncherJobs; |
| 682 return false; |
| 683 } |
| 684 |
| 685 parallel_jobs_ = jobs; |
| 686 } |
| 687 fprintf(stdout, "Using %" PRIuS " parallel jobs.\n", parallel_jobs_); |
| 688 fflush(stdout); |
| 689 worker_pool_owner_.reset( |
| 690 new SequencedWorkerPoolOwner(parallel_jobs_, "test_launcher")); |
| 691 |
| 656 // Split --gtest_filter at '-', if there is one, to separate into | 692 // Split --gtest_filter at '-', if there is one, to separate into |
| 657 // positive filter and negative filter portions. | 693 // positive filter and negative filter portions. |
| 658 std::string filter = command_line->GetSwitchValueASCII(kGTestFilterFlag); | 694 std::string filter = command_line->GetSwitchValueASCII(kGTestFilterFlag); |
| 659 positive_test_filter_ = filter; | 695 positive_test_filter_ = filter; |
| 660 size_t dash_pos = filter.find('-'); | 696 size_t dash_pos = filter.find('-'); |
| 661 if (dash_pos != std::string::npos) { | 697 if (dash_pos != std::string::npos) { |
| 662 // Everything up to the dash. | 698 // Everything up to the dash. |
| 663 positive_test_filter_ = filter.substr(0, dash_pos); | 699 positive_test_filter_ = filter.substr(0, dash_pos); |
| 664 | 700 |
| 665 // Everything after the dash. | 701 // Everything after the dash. |
| (...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 951 | 987 |
| 952 g_live_processes.Get().erase(process_handle); | 988 g_live_processes.Get().erase(process_handle); |
| 953 } | 989 } |
| 954 | 990 |
| 955 base::CloseProcessHandle(process_handle); | 991 base::CloseProcessHandle(process_handle); |
| 956 | 992 |
| 957 return exit_code; | 993 return exit_code; |
| 958 } | 994 } |
| 959 | 995 |
| 960 } // namespace base | 996 } // namespace base |
| OLD | NEW |