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/unit_test_launcher.h" | 5 #include "base/test/launcher/unit_test_launcher.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 } | 237 } |
238 | 238 |
239 bool UnitTestLauncherDelegate::GetTests(std::vector<SplitTestName>* output) { | 239 bool UnitTestLauncherDelegate::GetTests(std::vector<SplitTestName>* output) { |
240 DCHECK(thread_checker_.CalledOnValidThread()); | 240 DCHECK(thread_checker_.CalledOnValidThread()); |
241 *output = GetCompiledInTests(); | 241 *output = GetCompiledInTests(); |
242 return true; | 242 return true; |
243 } | 243 } |
244 | 244 |
245 bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name, | 245 bool UnitTestLauncherDelegate::ShouldRunTest(const std::string& test_case_name, |
246 const std::string& test_name) { | 246 const std::string& test_name) { |
247 DCHECK(thread_checker_.CalledOnValidThread()); | 247 DCHECK(thread_checker_.CalledOnValidThread()); |
248 | 248 |
249 // There is no additional logic to disable specific tests. | 249 // There is no additional logic to disable specific tests. |
250 return true; | 250 return true; |
| 251 } |
| 252 |
| 253 size_t UnitTestLauncherDelegate::RunTests( |
| 254 TestLauncher* test_launcher, |
| 255 const std::vector<std::string>& test_names) { |
| 256 DCHECK(thread_checker_.CalledOnValidThread()); |
| 257 |
| 258 std::vector<std::string> batch; |
| 259 for (size_t i = 0; i < test_names.size(); i++) { |
| 260 batch.push_back(test_names[i]); |
| 261 |
| 262 if (batch.size() >= batch_limit_) { |
| 263 RunBatch(test_launcher, batch); |
| 264 batch.clear(); |
| 265 } |
251 } | 266 } |
252 | 267 |
253 size_t UnitTestLauncherDelegate::RunTests( | 268 RunBatch(test_launcher, batch); |
254 TestLauncher* test_launcher, | 269 |
255 const std::vector<std::string>& test_names) { | 270 return test_names.size(); |
256 DCHECK(thread_checker_.CalledOnValidThread()); | 271 } |
257 | 272 |
| 273 size_t UnitTestLauncherDelegate::RetryTests( |
| 274 TestLauncher* test_launcher, |
| 275 const std::vector<std::string>& test_names) { |
| 276 MessageLoop::current()->PostTask( |
| 277 FROM_HERE, Bind(&UnitTestLauncherDelegate::RunSerially, Unretained(this), |
| 278 test_launcher, test_names)); |
| 279 return test_names.size(); |
| 280 } |
| 281 |
| 282 void UnitTestLauncherDelegate::RunSerially( |
| 283 TestLauncher* test_launcher, |
| 284 const std::vector<std::string>& test_names) { |
| 285 if (test_names.empty()) |
| 286 return; |
| 287 |
| 288 std::vector<std::string> new_test_names(test_names); |
| 289 std::string test_name(new_test_names.back()); |
| 290 new_test_names.pop_back(); |
| 291 |
| 292 // Create a dedicated temporary directory to store the xml result data |
| 293 // per run to ensure clean state and make it possible to launch multiple |
| 294 // processes in parallel. |
| 295 base::FilePath output_file; |
| 296 CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file)); |
| 297 output_file = output_file.AppendASCII("test_results.xml"); |
| 298 |
| 299 std::vector<std::string> current_test_names; |
| 300 current_test_names.push_back(test_name); |
| 301 CommandLine cmd_line( |
| 302 GetCommandLineForChildGTestProcess(current_test_names, output_file)); |
| 303 |
| 304 GTestCallbackState callback_state; |
| 305 callback_state.test_launcher = test_launcher; |
| 306 callback_state.test_names = current_test_names; |
| 307 callback_state.output_file = output_file; |
| 308 |
| 309 test_launcher->LaunchChildGTestProcess( |
| 310 cmd_line, std::string(), TestTimeouts::test_launcher_timeout(), |
| 311 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, |
| 312 Bind(&UnitTestLauncherDelegate::SerialGTestCallback, Unretained(this), |
| 313 callback_state, new_test_names)); |
| 314 } |
| 315 |
| 316 void UnitTestLauncherDelegate::RunBatch( |
| 317 TestLauncher* test_launcher, |
| 318 const std::vector<std::string>& test_names) { |
| 319 DCHECK(thread_checker_.CalledOnValidThread()); |
| 320 |
| 321 if (test_names.empty()) |
| 322 return; |
| 323 |
| 324 // Create a dedicated temporary directory to store the xml result data |
| 325 // per run to ensure clean state and make it possible to launch multiple |
| 326 // processes in parallel. |
| 327 base::FilePath output_file; |
| 328 CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file)); |
| 329 output_file = output_file.AppendASCII("test_results.xml"); |
| 330 |
| 331 CommandLine cmd_line( |
| 332 GetCommandLineForChildGTestProcess(test_names, output_file)); |
| 333 |
| 334 // Adjust the timeout depending on how many tests we're running |
| 335 // (note that e.g. the last batch of tests will be smaller). |
| 336 // TODO(phajdan.jr): Consider an adaptive timeout, which can change |
| 337 // depending on how many tests ran and how many remain. |
| 338 // Note: do NOT parse child's stdout to do that, it's known to be |
| 339 // unreliable (e.g. buffering issues can mix up the output). |
| 340 base::TimeDelta timeout = |
| 341 test_names.size() * TestTimeouts::test_launcher_timeout(); |
| 342 |
| 343 GTestCallbackState callback_state; |
| 344 callback_state.test_launcher = test_launcher; |
| 345 callback_state.test_names = test_names; |
| 346 callback_state.output_file = output_file; |
| 347 |
| 348 test_launcher->LaunchChildGTestProcess( |
| 349 cmd_line, std::string(), timeout, |
| 350 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, |
| 351 Bind(&UnitTestLauncherDelegate::GTestCallback, Unretained(this), |
| 352 callback_state)); |
| 353 } |
| 354 |
| 355 void UnitTestLauncherDelegate::GTestCallback( |
| 356 const GTestCallbackState& callback_state, |
| 357 int exit_code, |
| 358 const TimeDelta& elapsed_time, |
| 359 bool was_timeout, |
| 360 const std::string& output) { |
| 361 DCHECK(thread_checker_.CalledOnValidThread()); |
| 362 std::vector<std::string> tests_to_relaunch; |
| 363 ProcessTestResults(callback_state.test_launcher, callback_state.test_names, |
| 364 callback_state.output_file, output, exit_code, was_timeout, |
| 365 &tests_to_relaunch); |
| 366 |
| 367 // Relaunch requested tests in parallel, but only use single |
| 368 // test per batch for more precise results (crashes, test passes |
| 369 // but non-zero exit codes etc). |
| 370 for (size_t i = 0; i < tests_to_relaunch.size(); i++) { |
258 std::vector<std::string> batch; | 371 std::vector<std::string> batch; |
| 372 batch.push_back(tests_to_relaunch[i]); |
| 373 RunBatch(callback_state.test_launcher, batch); |
| 374 } |
| 375 |
| 376 // The temporary file's directory is also temporary. |
| 377 DeleteFile(callback_state.output_file.DirName(), true); |
| 378 } |
| 379 |
| 380 void UnitTestLauncherDelegate::SerialGTestCallback( |
| 381 const GTestCallbackState& callback_state, |
| 382 const std::vector<std::string>& test_names, |
| 383 int exit_code, |
| 384 const TimeDelta& elapsed_time, |
| 385 bool was_timeout, |
| 386 const std::string& output) { |
| 387 DCHECK(thread_checker_.CalledOnValidThread()); |
| 388 std::vector<std::string> tests_to_relaunch; |
| 389 bool called_any_callbacks = |
| 390 ProcessTestResults(callback_state.test_launcher, |
| 391 callback_state.test_names, callback_state.output_file, |
| 392 output, exit_code, was_timeout, &tests_to_relaunch); |
| 393 |
| 394 // There is only one test, there cannot be other tests to relaunch |
| 395 // due to a crash. |
| 396 DCHECK(tests_to_relaunch.empty()); |
| 397 |
| 398 // There is only one test, we should have called back with its result. |
| 399 DCHECK(called_any_callbacks); |
| 400 |
| 401 // The temporary file's directory is also temporary. |
| 402 DeleteFile(callback_state.output_file.DirName(), true); |
| 403 |
| 404 MessageLoop::current()->PostTask( |
| 405 FROM_HERE, Bind(&UnitTestLauncherDelegate::RunSerially, Unretained(this), |
| 406 callback_state.test_launcher, test_names)); |
| 407 } |
| 408 |
| 409 // static |
| 410 bool UnitTestLauncherDelegate::ProcessTestResults( |
| 411 TestLauncher* test_launcher, |
| 412 const std::vector<std::string>& test_names, |
| 413 const base::FilePath& output_file, |
| 414 const std::string& output, |
| 415 int exit_code, |
| 416 bool was_timeout, |
| 417 std::vector<std::string>* tests_to_relaunch) { |
| 418 std::vector<TestResult> test_results; |
| 419 bool crashed = false; |
| 420 bool have_test_results = |
| 421 ProcessGTestOutput(output_file, &test_results, &crashed); |
| 422 |
| 423 bool called_any_callback = false; |
| 424 |
| 425 if (have_test_results) { |
| 426 // TODO(phajdan.jr): Check for duplicates and mismatches between |
| 427 // the results we got from XML file and tests we intended to run. |
| 428 std::map<std::string, TestResult> results_map; |
| 429 for (size_t i = 0; i < test_results.size(); i++) |
| 430 results_map[test_results[i].full_name] = test_results[i]; |
| 431 |
| 432 bool had_interrupted_test = false; |
| 433 |
| 434 // Results to be reported back to the test launcher. |
| 435 std::vector<TestResult> final_results; |
| 436 |
259 for (size_t i = 0; i < test_names.size(); i++) { | 437 for (size_t i = 0; i < test_names.size(); i++) { |
260 batch.push_back(test_names[i]); | 438 if (ContainsKey(results_map, test_names[i])) { |
261 | 439 TestResult test_result = results_map[test_names[i]]; |
262 if (batch.size() >= batch_limit_) { | 440 if (test_result.status == TestResult::TEST_CRASH) { |
263 RunBatch(test_launcher, batch); | 441 had_interrupted_test = true; |
264 batch.clear(); | 442 |
265 } | 443 if (was_timeout) { |
266 } | 444 // Fix up the test status: we forcibly kill the child process |
267 | 445 // after the timeout, so from XML results it looks just like |
268 RunBatch(test_launcher, batch); | 446 // a crash. |
269 | 447 test_result.status = TestResult::TEST_TIMEOUT; |
270 return test_names.size(); | |
271 } | |
272 | |
273 size_t UnitTestLauncherDelegate::RetryTests( | |
274 TestLauncher* test_launcher, | |
275 const std::vector<std::string>& test_names) { | |
276 MessageLoop::current()->PostTask( | |
277 FROM_HERE, | |
278 Bind(&UnitTestLauncherDelegate::RunSerially, | |
279 Unretained(this), | |
280 test_launcher, | |
281 test_names)); | |
282 return test_names.size(); | |
283 } | |
284 | |
285 void UnitTestLauncherDelegate::RunSerially( | |
286 TestLauncher* test_launcher, | |
287 const std::vector<std::string>& test_names) { | |
288 if (test_names.empty()) | |
289 return; | |
290 | |
291 std::vector<std::string> new_test_names(test_names); | |
292 std::string test_name(new_test_names.back()); | |
293 new_test_names.pop_back(); | |
294 | |
295 // Create a dedicated temporary directory to store the xml result data | |
296 // per run to ensure clean state and make it possible to launch multiple | |
297 // processes in parallel. | |
298 base::FilePath output_file; | |
299 CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file)); | |
300 output_file = output_file.AppendASCII("test_results.xml"); | |
301 | |
302 std::vector<std::string> current_test_names; | |
303 current_test_names.push_back(test_name); | |
304 CommandLine cmd_line( | |
305 GetCommandLineForChildGTestProcess(current_test_names, output_file)); | |
306 | |
307 GTestCallbackState callback_state; | |
308 callback_state.test_launcher = test_launcher; | |
309 callback_state.test_names = current_test_names; | |
310 callback_state.output_file = output_file; | |
311 | |
312 test_launcher->LaunchChildGTestProcess( | |
313 cmd_line, | |
314 std::string(), | |
315 TestTimeouts::test_launcher_timeout(), | |
316 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, | |
317 Bind(&UnitTestLauncherDelegate::SerialGTestCallback, | |
318 Unretained(this), | |
319 callback_state, | |
320 new_test_names)); | |
321 } | |
322 | |
323 void UnitTestLauncherDelegate::RunBatch( | |
324 TestLauncher* test_launcher, | |
325 const std::vector<std::string>& test_names) { | |
326 DCHECK(thread_checker_.CalledOnValidThread()); | |
327 | |
328 if (test_names.empty()) | |
329 return; | |
330 | |
331 // Create a dedicated temporary directory to store the xml result data | |
332 // per run to ensure clean state and make it possible to launch multiple | |
333 // processes in parallel. | |
334 base::FilePath output_file; | |
335 CHECK(CreateNewTempDirectory(FilePath::StringType(), &output_file)); | |
336 output_file = output_file.AppendASCII("test_results.xml"); | |
337 | |
338 CommandLine cmd_line( | |
339 GetCommandLineForChildGTestProcess(test_names, output_file)); | |
340 | |
341 // Adjust the timeout depending on how many tests we're running | |
342 // (note that e.g. the last batch of tests will be smaller). | |
343 // TODO(phajdan.jr): Consider an adaptive timeout, which can change | |
344 // depending on how many tests ran and how many remain. | |
345 // Note: do NOT parse child's stdout to do that, it's known to be | |
346 // unreliable (e.g. buffering issues can mix up the output). | |
347 base::TimeDelta timeout = | |
348 test_names.size() * TestTimeouts::test_launcher_timeout(); | |
349 | |
350 GTestCallbackState callback_state; | |
351 callback_state.test_launcher = test_launcher; | |
352 callback_state.test_names = test_names; | |
353 callback_state.output_file = output_file; | |
354 | |
355 test_launcher->LaunchChildGTestProcess( | |
356 cmd_line, | |
357 std::string(), | |
358 timeout, | |
359 use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, | |
360 Bind(&UnitTestLauncherDelegate::GTestCallback, | |
361 Unretained(this), | |
362 callback_state)); | |
363 } | |
364 | |
365 void UnitTestLauncherDelegate::GTestCallback( | |
366 const GTestCallbackState& callback_state, | |
367 int exit_code, | |
368 const TimeDelta& elapsed_time, | |
369 bool was_timeout, | |
370 const std::string& output) { | |
371 DCHECK(thread_checker_.CalledOnValidThread()); | |
372 std::vector<std::string> tests_to_relaunch; | |
373 ProcessTestResults(callback_state.test_launcher, | |
374 callback_state.test_names, | |
375 callback_state.output_file, | |
376 output, | |
377 exit_code, | |
378 was_timeout, | |
379 &tests_to_relaunch); | |
380 | |
381 // Relaunch requested tests in parallel, but only use single | |
382 // test per batch for more precise results (crashes, test passes | |
383 // but non-zero exit codes etc). | |
384 for (size_t i = 0; i < tests_to_relaunch.size(); i++) { | |
385 std::vector<std::string> batch; | |
386 batch.push_back(tests_to_relaunch[i]); | |
387 RunBatch(callback_state.test_launcher, batch); | |
388 } | |
389 | |
390 // The temporary file's directory is also temporary. | |
391 DeleteFile(callback_state.output_file.DirName(), true); | |
392 } | |
393 | |
394 void UnitTestLauncherDelegate::SerialGTestCallback( | |
395 const GTestCallbackState& callback_state, | |
396 const std::vector<std::string>& test_names, | |
397 int exit_code, | |
398 const TimeDelta& elapsed_time, | |
399 bool was_timeout, | |
400 const std::string& output) { | |
401 DCHECK(thread_checker_.CalledOnValidThread()); | |
402 std::vector<std::string> tests_to_relaunch; | |
403 bool called_any_callbacks = | |
404 ProcessTestResults(callback_state.test_launcher, | |
405 callback_state.test_names, | |
406 callback_state.output_file, | |
407 output, | |
408 exit_code, | |
409 was_timeout, | |
410 &tests_to_relaunch); | |
411 | |
412 // There is only one test, there cannot be other tests to relaunch | |
413 // due to a crash. | |
414 DCHECK(tests_to_relaunch.empty()); | |
415 | |
416 // There is only one test, we should have called back with its result. | |
417 DCHECK(called_any_callbacks); | |
418 | |
419 // The temporary file's directory is also temporary. | |
420 DeleteFile(callback_state.output_file.DirName(), true); | |
421 | |
422 MessageLoop::current()->PostTask( | |
423 FROM_HERE, | |
424 Bind(&UnitTestLauncherDelegate::RunSerially, | |
425 Unretained(this), | |
426 callback_state.test_launcher, | |
427 test_names)); | |
428 } | |
429 | |
430 // static | |
431 bool UnitTestLauncherDelegate::ProcessTestResults( | |
432 TestLauncher* test_launcher, | |
433 const std::vector<std::string>& test_names, | |
434 const base::FilePath& output_file, | |
435 const std::string& output, | |
436 int exit_code, | |
437 bool was_timeout, | |
438 std::vector<std::string>* tests_to_relaunch) { | |
439 std::vector<TestResult> test_results; | |
440 bool crashed = false; | |
441 bool have_test_results = | |
442 ProcessGTestOutput(output_file, &test_results, &crashed); | |
443 | |
444 bool called_any_callback = false; | |
445 | |
446 if (have_test_results) { | |
447 // TODO(phajdan.jr): Check for duplicates and mismatches between | |
448 // the results we got from XML file and tests we intended to run. | |
449 std::map<std::string, TestResult> results_map; | |
450 for (size_t i = 0; i < test_results.size(); i++) | |
451 results_map[test_results[i].full_name] = test_results[i]; | |
452 | |
453 bool had_interrupted_test = false; | |
454 | |
455 // Results to be reported back to the test launcher. | |
456 std::vector<TestResult> final_results; | |
457 | |
458 for (size_t i = 0; i < test_names.size(); i++) { | |
459 if (ContainsKey(results_map, test_names[i])) { | |
460 TestResult test_result = results_map[test_names[i]]; | |
461 if (test_result.status == TestResult::TEST_CRASH) { | |
462 had_interrupted_test = true; | |
463 | |
464 if (was_timeout) { | |
465 // Fix up the test status: we forcibly kill the child process | |
466 // after the timeout, so from XML results it looks just like | |
467 // a crash. | |
468 test_result.status = TestResult::TEST_TIMEOUT; | |
469 } | |
470 } else if (test_result.status == TestResult::TEST_SUCCESS || | |
471 test_result.status == TestResult::TEST_FAILURE) { | |
472 // We run multiple tests in a batch with a timeout applied | |
473 // to the entire batch. It is possible that with other tests | |
474 // running quickly some tests take longer than the per-test timeout. | |
475 // For consistent handling of tests independent of order and other | |
476 // factors, mark them as timing out. | |
477 if (test_result.elapsed_time > | |
478 TestTimeouts::test_launcher_timeout()) { | |
479 test_result.status = TestResult::TEST_TIMEOUT; | |
480 } | |
481 } | 448 } |
482 test_result.output_snippet = | 449 } else if (test_result.status == TestResult::TEST_SUCCESS || |
483 GetTestOutputSnippet(test_result, output); | 450 test_result.status == TestResult::TEST_FAILURE) { |
484 final_results.push_back(test_result); | 451 // We run multiple tests in a batch with a timeout applied |
485 } else if (had_interrupted_test) { | 452 // to the entire batch. It is possible that with other tests |
486 tests_to_relaunch->push_back(test_names[i]); | 453 // running quickly some tests take longer than the per-test timeout. |
487 } else { | 454 // For consistent handling of tests independent of order and other |
488 // TODO(phajdan.jr): Explicitly pass the info that the test didn't | 455 // factors, mark them as timing out. |
489 // run for a mysterious reason. | 456 if (test_result.elapsed_time > |
490 LOG(ERROR) << "no test result for " << test_names[i]; | 457 TestTimeouts::test_launcher_timeout()) { |
491 TestResult test_result; | 458 test_result.status = TestResult::TEST_TIMEOUT; |
492 test_result.full_name = test_names[i]; | 459 } |
493 test_result.status = TestResult::TEST_UNKNOWN; | |
494 test_result.output_snippet = | |
495 GetTestOutputSnippet(test_result, output); | |
496 final_results.push_back(test_result); | |
497 } | 460 } |
498 } | 461 test_result.output_snippet = GetTestOutputSnippet(test_result, output); |
499 | 462 final_results.push_back(test_result); |
500 // TODO(phajdan.jr): Handle the case where processing XML output | 463 } else if (had_interrupted_test) { |
501 // indicates a crash but none of the test results is marked as crashing. | 464 tests_to_relaunch->push_back(test_names[i]); |
502 | 465 } else { |
503 if (final_results.empty()) | 466 // TODO(phajdan.jr): Explicitly pass the info that the test didn't |
504 return false; | 467 // run for a mysterious reason. |
505 | 468 LOG(ERROR) << "no test result for " << test_names[i]; |
506 bool has_non_success_test = false; | |
507 for (size_t i = 0; i < final_results.size(); i++) { | |
508 if (final_results[i].status != TestResult::TEST_SUCCESS) { | |
509 has_non_success_test = true; | |
510 break; | |
511 } | |
512 } | |
513 | |
514 if (!has_non_success_test && exit_code != 0) { | |
515 // This is a bit surprising case: all tests are marked as successful, | |
516 // but the exit code was not zero. This can happen e.g. under memory | |
517 // tools that report leaks this way. | |
518 | |
519 if (final_results.size() == 1) { | |
520 // Easy case. One test only so we know the non-zero exit code | |
521 // was caused by that one test. | |
522 final_results[0].status = TestResult::TEST_FAILURE_ON_EXIT; | |
523 } else { | |
524 // Harder case. Discard the results and request relaunching all | |
525 // tests without batching. This will trigger above branch on | |
526 // relaunch leading to more precise results. | |
527 LOG(WARNING) << "Not sure which test caused non-zero exit code, " | |
528 << "relaunching all of them without batching."; | |
529 | |
530 for (size_t i = 0; i < final_results.size(); i++) | |
531 tests_to_relaunch->push_back(final_results[i].full_name); | |
532 | |
533 return false; | |
534 } | |
535 } | |
536 | |
537 for (size_t i = 0; i < final_results.size(); i++) { | |
538 // Fix the output snippet after possible changes to the test result. | |
539 final_results[i].output_snippet = | |
540 GetTestOutputSnippet(final_results[i], output); | |
541 test_launcher->OnTestFinished(final_results[i]); | |
542 called_any_callback = true; | |
543 } | |
544 } else { | |
545 fprintf(stdout, | |
546 "Failed to get out-of-band test success data, " | |
547 "dumping full stdio below:\n%s\n", | |
548 output.c_str()); | |
549 fflush(stdout); | |
550 | |
551 // We do not have reliable details about test results (parsing test | |
552 // stdout is known to be unreliable), apply the executable exit code | |
553 // to all tests. | |
554 // TODO(phajdan.jr): Be smarter about this, e.g. retry each test | |
555 // individually. | |
556 for (size_t i = 0; i < test_names.size(); i++) { | |
557 TestResult test_result; | 469 TestResult test_result; |
558 test_result.full_name = test_names[i]; | 470 test_result.full_name = test_names[i]; |
559 test_result.status = TestResult::TEST_UNKNOWN; | 471 test_result.status = TestResult::TEST_UNKNOWN; |
560 test_launcher->OnTestFinished(test_result); | 472 test_result.output_snippet = GetTestOutputSnippet(test_result, output); |
561 called_any_callback = true; | 473 final_results.push_back(test_result); |
562 } | 474 } |
563 } | 475 } |
564 | 476 |
565 return called_any_callback; | 477 // TODO(phajdan.jr): Handle the case where processing XML output |
| 478 // indicates a crash but none of the test results is marked as crashing. |
| 479 |
| 480 if (final_results.empty()) |
| 481 return false; |
| 482 |
| 483 bool has_non_success_test = false; |
| 484 for (size_t i = 0; i < final_results.size(); i++) { |
| 485 if (final_results[i].status != TestResult::TEST_SUCCESS) { |
| 486 has_non_success_test = true; |
| 487 break; |
| 488 } |
| 489 } |
| 490 |
| 491 if (!has_non_success_test && exit_code != 0) { |
| 492 // This is a bit surprising case: all tests are marked as successful, |
| 493 // but the exit code was not zero. This can happen e.g. under memory |
| 494 // tools that report leaks this way. |
| 495 |
| 496 if (final_results.size() == 1) { |
| 497 // Easy case. One test only so we know the non-zero exit code |
| 498 // was caused by that one test. |
| 499 final_results[0].status = TestResult::TEST_FAILURE_ON_EXIT; |
| 500 } else { |
| 501 // Harder case. Discard the results and request relaunching all |
| 502 // tests without batching. This will trigger above branch on |
| 503 // relaunch leading to more precise results. |
| 504 LOG(WARNING) << "Not sure which test caused non-zero exit code, " |
| 505 << "relaunching all of them without batching."; |
| 506 |
| 507 for (size_t i = 0; i < final_results.size(); i++) |
| 508 tests_to_relaunch->push_back(final_results[i].full_name); |
| 509 |
| 510 return false; |
| 511 } |
| 512 } |
| 513 |
| 514 for (size_t i = 0; i < final_results.size(); i++) { |
| 515 // Fix the output snippet after possible changes to the test result. |
| 516 final_results[i].output_snippet = |
| 517 GetTestOutputSnippet(final_results[i], output); |
| 518 test_launcher->OnTestFinished(final_results[i]); |
| 519 called_any_callback = true; |
| 520 } |
| 521 } else { |
| 522 fprintf(stdout, |
| 523 "Failed to get out-of-band test success data, " |
| 524 "dumping full stdio below:\n%s\n", |
| 525 output.c_str()); |
| 526 fflush(stdout); |
| 527 |
| 528 // We do not have reliable details about test results (parsing test |
| 529 // stdout is known to be unreliable), apply the executable exit code |
| 530 // to all tests. |
| 531 // TODO(phajdan.jr): Be smarter about this, e.g. retry each test |
| 532 // individually. |
| 533 for (size_t i = 0; i < test_names.size(); i++) { |
| 534 TestResult test_result; |
| 535 test_result.full_name = test_names[i]; |
| 536 test_result.status = TestResult::TEST_UNKNOWN; |
| 537 test_launcher->OnTestFinished(test_result); |
| 538 called_any_callback = true; |
| 539 } |
566 } | 540 } |
567 | 541 |
| 542 return called_any_callback; |
| 543 } |
| 544 |
568 } // namespace base | 545 } // namespace base |
OLD | NEW |