Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Classes and methods for executing tests. | 6 * Classes and methods for executing tests. |
| 7 * | 7 * |
| 8 * This module includes: | 8 * This module includes: |
| 9 * - Managing parallel execution of tests, including timeout checks. | 9 * - Managing parallel execution of tests, including timeout checks. |
| 10 * - Evaluating the output of each test as pass/fail/crash/timeout. | 10 * - Evaluating the output of each test as pass/fail/crash/timeout. |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 25 typedef void ExitCodeEvent(int exitCode); | 25 typedef void ExitCodeEvent(int exitCode); |
| 26 typedef void EnqueueMoreWork(ProcessQueue queue); | 26 typedef void EnqueueMoreWork(ProcessQueue queue); |
| 27 | 27 |
| 28 /** A command executed as a step in a test case. */ | 28 /** A command executed as a step in a test case. */ |
| 29 class Command { | 29 class Command { |
| 30 /** Path to the executable of this command. */ | 30 /** Path to the executable of this command. */ |
| 31 String executable; | 31 String executable; |
| 32 | 32 |
| 33 /** Command line arguments to the executable. */ | 33 /** Command line arguments to the executable. */ |
| 34 List<String> arguments; | 34 List<String> arguments; |
| 35 | |
| 36 /** Environment for the command */ | |
| 37 Map<String,String> environment; | |
| 35 | 38 |
| 36 /** The actual command line that will be executed. */ | 39 /** The actual command line that will be executed. */ |
| 37 String commandLine; | 40 String commandLine; |
| 38 | 41 |
| 39 Command(this.executable, this.arguments) { | 42 Command(this.executable, this.arguments, [this.environment = null]) { |
| 40 if (Platform.operatingSystem == 'windows') { | 43 if (Platform.operatingSystem == 'windows') { |
| 41 // Windows can't handle the first command if it is a .bat file or the like | 44 // Windows can't handle the first command if it is a .bat file or the like |
| 42 // with the slashes going the other direction. | 45 // with the slashes going the other direction. |
| 43 // TODO(efortuna): Remove this when fixed (Issue 1306). | 46 // TODO(efortuna): Remove this when fixed (Issue 1306). |
| 44 executable = executable.replaceAll('/', '\\'); | 47 executable = executable.replaceAll('/', '\\'); |
| 45 } | 48 } |
| 46 commandLine = "$executable ${Strings.join(arguments, ' ')}"; | 49 commandLine = "$executable ${Strings.join(arguments, ' ')}"; |
| 47 } | 50 } |
| 48 | 51 |
| 49 String toString() => commandLine; | 52 String toString() => commandLine; |
| 50 | 53 |
| 51 Future<bool> get outputIsUpToDate => new Future.immediate(false); | 54 Future<bool> get outputIsUpToDate => new Future.immediate(false); |
| 55 Path get expectedOutputFile => null; | |
| 56 bool get isPixelTest => false; | |
| 52 } | 57 } |
| 53 | 58 |
| 54 class Dart2JsCommand extends Command { | 59 class Dart2JsCommand extends Command { |
| 55 String _jsOutputFile; | 60 String _jsOutputFile; |
| 56 bool _neverSkipCompilation; | 61 bool _neverSkipCompilation; |
| 57 List<Uri> _bootstrapDependencies; | 62 List<Uri> _bootstrapDependencies; |
| 58 | 63 |
| 59 Dart2JsCommand(this._jsOutputFile, | 64 Dart2JsCommand(this._jsOutputFile, |
| 60 this._neverSkipCompilation, | 65 this._neverSkipCompilation, |
| 61 this._bootstrapDependencies, | 66 this._bootstrapDependencies, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 98 } | 103 } |
| 99 } | 104 } |
| 100 return true; | 105 return true; |
| 101 } | 106 } |
| 102 } | 107 } |
| 103 return false; | 108 return false; |
| 104 }); | 109 }); |
| 105 } | 110 } |
| 106 } | 111 } |
| 107 | 112 |
| 113 class DumpRenderTreeCommand extends Command { | |
| 114 /** | |
| 115 * If [expectedOutputPath] is set, the output of DumpRenderTree is compared | |
| 116 * with the content of [expectedOutputPath]. | |
| 117 * This is used for example for pixel tests, where [expectedOutputPath] points | |
| 118 * to a *png file. | |
| 119 */ | |
| 120 Path expectedOutputPath; | |
| 121 | |
| 122 DumpRenderTreeCommand(String executable, | |
| 123 String htmlFile, | |
| 124 List<String> options, | |
| 125 List<String> dartFlags, | |
| 126 Uri packageRootUri, | |
| 127 Path this.expectedOutputPath) | |
| 128 : super(executable, | |
| 129 _getArguments(options, htmlFile), | |
| 130 _getEnvironment(dartFlags, packageRootUri)); | |
| 131 | |
| 132 static Map _getEnvironment(List<String> dartFlags, Uri packageRootUri) { | |
| 133 var needDartFlags = dartFlags != null && dartFlags.length > 0; | |
| 134 var needDartPackageRoot = packageRootUri != null; | |
| 135 | |
| 136 var env = null; | |
| 137 if (needDartFlags || needDartPackageRoot) { | |
| 138 var env = new Map.from(Platform.environment); | |
| 139 if (needDartFlags) { | |
| 140 env['DART_FLAGS'] = Strings.join(dartFlags, " "); | |
| 141 } | |
| 142 if (needDartPackageRoot) { | |
| 143 env['DART_PACKAGE_ROOT'] = packageRootUri.toString(); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 return env; | |
| 148 } | |
| 149 | |
| 150 static List<String> _getArguments(List<String> options, String htmlFile) { | |
| 151 var arguments = new List.from(options); | |
| 152 arguments.add(htmlFile); | |
| 153 return arguments; | |
| 154 } | |
| 155 | |
| 156 Path get expectedOutputFile => expectedOutputPath; | |
| 157 bool get isPixelTest => (expectedOutputFile != null && | |
| 158 expectedOutputFile.filename.endsWith(".png")); | |
| 159 } | |
| 160 | |
| 161 | |
| 108 /** | 162 /** |
| 109 * TestCase contains all the information needed to run a test and evaluate | 163 * TestCase contains all the information needed to run a test and evaluate |
| 110 * its output. Running a test involves starting a separate process, with | 164 * its output. Running a test involves starting a separate process, with |
| 111 * the executable and arguments given by the TestCase, and recording its | 165 * the executable and arguments given by the TestCase, and recording its |
| 112 * stdout and stderr output streams, and its exit code. TestCase only | 166 * stdout and stderr output streams, and its exit code. TestCase only |
| 113 * contains static information about the test; actually running the test is | 167 * contains static information about the test; actually running the test is |
| 114 * performed by [ProcessQueue] using a [RunningProcess] object. | 168 * performed by [ProcessQueue] using a [RunningProcess] object. |
| 115 * | 169 * |
| 116 * The output information is stored in a [CommandOutput] instance contained | 170 * The output information is stored in a [CommandOutput] instance contained |
| 117 * in TestCase.commandOutputs. The last CommandOutput instance is responsible | 171 * in TestCase.commandOutputs. The last CommandOutput instance is responsible |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 310 * code, the standard output and standard error, whether the process timed out, | 364 * code, the standard output and standard error, whether the process timed out, |
| 311 * and the time the process took to run. It also contains a pointer to the | 365 * and the time the process took to run. It also contains a pointer to the |
| 312 * [TestCase] this is the output of. | 366 * [TestCase] this is the output of. |
| 313 */ | 367 */ |
| 314 abstract class CommandOutput { | 368 abstract class CommandOutput { |
| 315 factory CommandOutput.fromCase(TestCase testCase, | 369 factory CommandOutput.fromCase(TestCase testCase, |
| 316 Command command, | 370 Command command, |
| 317 int exitCode, | 371 int exitCode, |
| 318 bool incomplete, | 372 bool incomplete, |
| 319 bool timedOut, | 373 bool timedOut, |
| 320 List<String> stdout, | 374 List<int> stdout, |
| 321 List<String> stderr, | 375 List<int> stderr, |
| 322 Duration time, | 376 Duration time, |
| 323 bool compilationSkipped) { | 377 bool compilationSkipped) { |
| 324 return new CommandOutputImpl.fromCase(testCase, | 378 return new CommandOutputImpl.fromCase(testCase, |
| 325 command, | 379 command, |
| 326 exitCode, | 380 exitCode, |
| 327 incomplete, | 381 incomplete, |
| 328 timedOut, | 382 timedOut, |
| 329 stdout, | 383 stdout, |
| 330 stderr, | 384 stderr, |
| 331 time, | 385 time, |
| 332 compilationSkipped); | 386 compilationSkipped); |
| 333 } | 387 } |
| 334 | 388 |
| 389 Command get command; | |
| 390 | |
| 335 bool get incomplete; | 391 bool get incomplete; |
| 336 | 392 |
| 337 String get result; | 393 String get result; |
| 338 | 394 |
| 339 bool get unexpectedOutput; | 395 bool get unexpectedOutput; |
| 340 | 396 |
| 341 bool get hasCrashed; | 397 bool get hasCrashed; |
| 342 | 398 |
| 343 bool get hasTimedOut; | 399 bool get hasTimedOut; |
| 344 | 400 |
| 345 bool get didFail; | 401 bool get didFail; |
| 346 | 402 |
| 347 bool requestRetry; | 403 bool requestRetry; |
| 348 | 404 |
| 349 Duration get time; | 405 Duration get time; |
| 350 | 406 |
| 351 int get exitCode; | 407 int get exitCode; |
| 352 | 408 |
| 353 List<String> get stdout; | 409 List<int> get stdout; |
| 354 | 410 |
| 355 List<String> get stderr; | 411 List<int> get stderr; |
| 356 | 412 |
| 357 List<String> get diagnostics; | 413 List<String> get diagnostics; |
| 358 | 414 |
| 359 bool get compilationSkipped; | 415 bool get compilationSkipped; |
| 360 } | 416 } |
| 361 | 417 |
| 362 class CommandOutputImpl implements CommandOutput { | 418 class CommandOutputImpl implements CommandOutput { |
| 419 Command command; | |
| 363 TestCase testCase; | 420 TestCase testCase; |
| 364 int exitCode; | 421 int exitCode; |
| 365 | 422 |
| 366 /// Records if all commands were run, true if they weren't. | 423 /// Records if all commands were run, true if they weren't. |
| 367 final bool incomplete; | 424 final bool incomplete; |
| 368 | 425 |
| 369 bool timedOut; | 426 bool timedOut; |
| 370 bool failed = false; | 427 bool failed = false; |
| 371 List<String> stdout; | 428 List<int> stdout; |
| 372 List<String> stderr; | 429 List<int> stderr; |
| 373 Duration time; | 430 Duration time; |
| 374 List<String> diagnostics; | 431 List<String> diagnostics; |
| 375 bool compilationSkipped; | 432 bool compilationSkipped; |
| 376 | 433 |
| 377 /** | 434 /** |
| 378 * A flag to indicate we have already printed a warning about ignoring the VM | 435 * A flag to indicate we have already printed a warning about ignoring the VM |
| 379 * crash, to limit the amount of output produced per test. | 436 * crash, to limit the amount of output produced per test. |
| 380 */ | 437 */ |
| 381 bool alreadyPrintedWarning = false; | 438 bool alreadyPrintedWarning = false; |
| 382 | 439 |
| 383 /** | 440 /** |
| 384 * Set to true if we encounter a condition in the output that indicates we | 441 * Set to true if we encounter a condition in the output that indicates we |
| 385 * need to rerun this test. | 442 * need to rerun this test. |
| 386 */ | 443 */ |
| 387 bool requestRetry = false; | 444 bool requestRetry = false; |
| 388 | 445 |
| 389 // Don't call this constructor, call CommandOutput.fromCase() to | 446 // Don't call this constructor, call CommandOutput.fromCase() to |
| 390 // get a new TestOutput instance. | 447 // get a new TestOutput instance. |
| 391 CommandOutputImpl(TestCase this.testCase, | 448 CommandOutputImpl(TestCase this.testCase, |
| 392 Command command, | 449 Command this.command, |
| 393 int this.exitCode, | 450 int this.exitCode, |
| 394 bool this.incomplete, | 451 bool this.incomplete, |
| 395 bool this.timedOut, | 452 bool this.timedOut, |
| 396 List<String> this.stdout, | 453 List<int> this.stdout, |
| 397 List<String> this.stderr, | 454 List<int> this.stderr, |
| 398 Duration this.time, | 455 Duration this.time, |
| 399 bool this.compilationSkipped) { | 456 bool this.compilationSkipped) { |
| 400 testCase.commandOutputs[command] = this; | 457 testCase.commandOutputs[command] = this; |
| 401 diagnostics = []; | 458 diagnostics = []; |
| 402 } | 459 } |
| 403 factory CommandOutputImpl.fromCase(TestCase testCase, | 460 factory CommandOutputImpl.fromCase(TestCase testCase, |
| 404 Command command, | 461 Command command, |
| 405 int exitCode, | 462 int exitCode, |
| 406 bool incomplete, | 463 bool incomplete, |
| 407 bool timedOut, | 464 bool timedOut, |
| 408 List<String> stdout, | 465 List<int> stdout, |
| 409 List<String> stderr, | 466 List<int> stderr, |
| 410 Duration time, | 467 Duration time, |
| 411 bool compilationSkipped) { | 468 bool compilationSkipped) { |
| 412 if (testCase is BrowserTestCase) { | 469 if (testCase is BrowserTestCase) { |
| 413 return new BrowserCommandOutputImpl(testCase, | 470 return new BrowserCommandOutputImpl(testCase, |
| 414 command, | 471 command, |
| 415 exitCode, | 472 exitCode, |
| 416 incomplete, | 473 incomplete, |
| 417 timedOut, | 474 timedOut, |
| 418 stdout, | 475 stdout, |
| 419 stderr, | 476 stderr, |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 497 stderr, | 554 stderr, |
| 498 time, | 555 time, |
| 499 compilationSkipped); | 556 compilationSkipped); |
| 500 | 557 |
| 501 bool get didFail { | 558 bool get didFail { |
| 502 // Browser case: | 559 // Browser case: |
| 503 // If the browser test failed, it may have been because DumpRenderTree | 560 // If the browser test failed, it may have been because DumpRenderTree |
| 504 // and the virtual framebuffer X server didn't hook up, or DRT crashed with | 561 // and the virtual framebuffer X server didn't hook up, or DRT crashed with |
| 505 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, | 562 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, |
| 506 // so we have to do this check first. | 563 // so we have to do this check first. |
| 507 for (String line in super.stderr) { | 564 var stderrLines = new String.fromCharCodes(super.stderr).split("\n"); |
| 565 for (String line in stderrLines) { | |
| 508 if (line.contains('Gtk-WARNING **: cannot open display: :99') || | 566 if (line.contains('Gtk-WARNING **: cannot open display: :99') || |
| 509 line.contains('Failed to run command. return code=1')) { | 567 line.contains('Failed to run command. return code=1')) { |
| 510 // If we get the X server error, or DRT crashes with a core dump, retry | 568 // If we get the X server error, or DRT crashes with a core dump, retry |
| 511 // the test. | 569 // the test. |
| 512 if ((testCase as BrowserTestCase).numRetries > 0) { | 570 if ((testCase as BrowserTestCase).numRetries > 0) { |
| 513 requestRetry = true; | 571 requestRetry = true; |
| 514 } | 572 } |
| 515 return true; | 573 return true; |
| 516 } | 574 } |
| 517 } | 575 } |
| 518 | 576 |
| 519 // Browser tests fail unless stdout contains | 577 if (command.expectedOutputFile != null) { |
| 520 // 'Content-Type: text/plain' followed by 'PASS'. | 578 // We are either doing a pixel test or a layout test with DumpRenderTree |
|
ricow1
2012/12/17 14:20:54
move the body of this file out into a seperate met
kustermann
2012/12/17 16:11:18
Done.
| |
| 521 bool has_content_type = false; | |
| 522 for (String line in super.stdout) { | |
| 523 switch (line) { | |
| 524 case 'Content-Type: text/plain': | |
| 525 has_content_type = true; | |
| 526 break; | |
| 527 | 579 |
| 528 case 'PASS': | 580 int findBytes(List<int> data, List<int> pattern, [int startPos=0]) { |
| 529 if (has_content_type) { | 581 // TODO(kustermann): Use one of the fast string-matching algorithms! |
| 530 return (exitCode != 0 && !hasCrashed); | 582 for (int i=startPos; i < (data.length-pattern.length); i++) { |
| 583 bool found = true; | |
| 584 for (int j=0; j<pattern.length; j++) { | |
| 585 if (data[i+j] != pattern[j]) { | |
| 586 found = false; | |
| 587 } | |
| 531 } | 588 } |
| 532 break; | 589 if (found) { |
| 590 return i; | |
| 591 } | |
| 592 } | |
| 593 return -1; | |
| 533 } | 594 } |
| 595 | |
| 596 bool areByteArraysEqual(List<int> buffer1, | |
|
ricow1
2012/12/17 14:20:54
doc style comment explaining what this does
kustermann
2012/12/17 16:11:18
Done.
| |
| 597 int buffer1Offset, | |
| 598 List<int> buffer2, | |
| 599 int buffer2Offset, | |
| 600 int count) { | |
| 601 if ((buffer1Offset + count) > buffer1.length || | |
| 602 (buffer2Offset + count) > buffer2.length) { | |
| 603 return false; | |
| 604 } | |
| 605 | |
| 606 for (var i=0; i<count; i++) { | |
|
ricow1
2012/12/17 14:20:54
space around =, space around <
kustermann
2012/12/17 16:11:18
Done.
| |
| 607 if (buffer1[buffer1Offset + i] != buffer2[buffer2Offset + i]) { | |
| 608 return false; | |
| 609 } | |
| 610 } | |
| 611 return true; | |
| 612 } | |
| 613 | |
| 614 var stdout = testCase.commandOutputs[command].stdout; | |
| 615 var file = new File.fromPath(command.expectedOutputFile); | |
| 616 if (file.existsSync()) { | |
| 617 var bytesContentLength = "Content-Length:".charCodes; | |
| 618 var bytesNewLine = "\n".charCodes; | |
| 619 var bytesEOF = "#EOF\n".charCodes; | |
| 620 | |
| 621 var expectedContent = file.readAsBytesSync(); | |
| 622 | |
| 623 /* | |
| 624 * The output of DumpRenderTree is different for pixel tests than for | |
| 625 * layout tests. | |
| 626 * | |
| 627 * On a pixel test, the DRT output has the following format | |
| 628 * ...... | |
| 629 * ...... | |
| 630 * Content-Length: ...\n | |
| 631 * <*png data> | |
| 632 * #EOF\n | |
| 633 * So we need to get the byte-range of the png data first before | |
| 634 * comparing it with the content of the expected output file. | |
| 635 * | |
| 636 * On a layout tests, the DRT output is directly compared with the | |
| 637 * content of the expected output directly. | |
| 638 */ | |
| 639 if (command.expectedOutputFile.filename.endsWith(".png")) { | |
| 640 var startOfContentLength = findBytes(stdout, bytesContentLength); | |
| 641 if (startOfContentLength >= 0) { | |
| 642 var newLineAfterContentLength = findBytes(stdout, | |
| 643 bytesNewLine, | |
| 644 startOfContentLength); | |
| 645 if (newLineAfterContentLength > 0) { | |
| 646 var startPosition = newLineAfterContentLength + | |
| 647 bytesNewLine.length; | |
| 648 var endPosition = stdout.length - bytesEOF.length; | |
| 649 | |
| 650 return !areByteArraysEqual(expectedContent, | |
| 651 0, | |
| 652 stdout, | |
| 653 startPosition, | |
| 654 endPosition - startPosition); | |
| 655 } | |
| 656 } | |
| 657 return true; | |
| 658 } else { | |
| 659 return !areByteArraysEqual(expectedContent, 0, | |
| 660 stdout, 0, | |
| 661 stdout.length); | |
| 662 } | |
| 663 } | |
| 664 return true; | |
| 665 } else { | |
| 666 // Browser tests fail unless stdout contains | |
| 667 // 'Content-Type: text/plain' followed by 'PASS'. | |
| 668 bool has_content_type = false; | |
| 669 var stdoutLines = new String.fromCharCodes(super.stdout).split("\n"); | |
| 670 for (String line in stdoutLines) { | |
| 671 switch (line) { | |
| 672 case 'Content-Type: text/plain': | |
| 673 has_content_type = true; | |
| 674 break; | |
| 675 | |
| 676 case 'PASS': | |
| 677 if (has_content_type) { | |
| 678 return (exitCode != 0 && !hasCrashed); | |
| 679 } | |
| 680 break; | |
| 681 } | |
| 682 } | |
| 683 return true; | |
| 534 } | 684 } |
| 535 return true; | |
| 536 } | 685 } |
| 537 } | 686 } |
| 538 | 687 |
| 539 // The static analyzer does not actually execute code, so | 688 // The static analyzer does not actually execute code, so |
| 540 // the criteria for success now depend on the text sent | 689 // the criteria for success now depend on the text sent |
| 541 // to stderr. | 690 // to stderr. |
| 542 class AnalysisCommandOutputImpl extends CommandOutputImpl { | 691 class AnalysisCommandOutputImpl extends CommandOutputImpl { |
| 543 // An error line has 8 fields that look like: | 692 // An error line has 8 fields that look like: |
| 544 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. | 693 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. |
| 545 final int ERROR_LEVEL = 0; | 694 final int ERROR_LEVEL = 0; |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 575 return failResult; | 724 return failResult; |
| 576 } | 725 } |
| 577 | 726 |
| 578 bool _didFail() { | 727 bool _didFail() { |
| 579 if (hasCrashed) return false; | 728 if (hasCrashed) return false; |
| 580 | 729 |
| 581 List<String> errors = []; | 730 List<String> errors = []; |
| 582 List<String> staticWarnings = []; | 731 List<String> staticWarnings = []; |
| 583 | 732 |
| 584 // Read the returned list of errors and stuff them away. | 733 // Read the returned list of errors and stuff them away. |
| 585 for (String line in super.stderr) { | 734 var stderrLines = new String.fromCharCodes(super.stderr).split("\n"); |
| 735 for (String line in stderrLines) { | |
| 586 if (line.length == 0) continue; | 736 if (line.length == 0) continue; |
| 587 List<String> fields = splitMachineError(line); | 737 List<String> fields = splitMachineError(line); |
| 588 if (fields[ERROR_LEVEL] == 'ERROR') { | 738 if (fields[ERROR_LEVEL] == 'ERROR') { |
| 589 errors.add(fields[FORMATTED_ERROR]); | 739 errors.add(fields[FORMATTED_ERROR]); |
| 590 } else if (fields[ERROR_LEVEL] == 'WARNING') { | 740 } else if (fields[ERROR_LEVEL] == 'WARNING') { |
| 591 // We only care about testing Static type warnings | 741 // We only care about testing Static type warnings |
| 592 // ignore all others | 742 // ignore all others |
| 593 if (fields[ERROR_TYPE] == 'STATIC_TYPE') { | 743 if (fields[ERROR_TYPE] == 'STATIC_TYPE') { |
| 594 staticWarnings.add(fields[FORMATTED_ERROR]); | 744 staticWarnings.add(fields[FORMATTED_ERROR]); |
| 595 } | 745 } |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 724 * the result; there are no pointers to it, so it should be available to | 874 * the result; there are no pointers to it, so it should be available to |
| 725 * be garbage collected as soon as it is done. | 875 * be garbage collected as soon as it is done. |
| 726 */ | 876 */ |
| 727 class RunningProcess { | 877 class RunningProcess { |
| 728 ProcessQueue processQueue; | 878 ProcessQueue processQueue; |
| 729 Process process; | 879 Process process; |
| 730 TestCase testCase; | 880 TestCase testCase; |
| 731 bool timedOut = false; | 881 bool timedOut = false; |
| 732 Date startTime; | 882 Date startTime; |
| 733 Timer timeoutTimer; | 883 Timer timeoutTimer; |
| 734 List<String> stdout; | 884 List<int> stdout; |
| 735 List<String> stderr; | 885 List<int> stderr; |
| 886 List<String> notifications; | |
| 736 bool compilationSkipped; | 887 bool compilationSkipped; |
| 737 bool allowRetries; | 888 bool allowRetries; |
| 738 | 889 |
| 739 /** Which command of [testCase.commands] is currently being executed. */ | 890 /** Which command of [testCase.commands] is currently being executed. */ |
| 740 int currentStep; | 891 int currentStep; |
| 741 | 892 |
| 742 RunningProcess(TestCase this.testCase, | 893 RunningProcess(TestCase this.testCase, |
| 743 [this.allowRetries = false, this.processQueue]); | 894 [this.allowRetries = false, this.processQueue]); |
| 744 | 895 |
| 745 /** | 896 /** |
| 746 * Called when all commands are executed. | 897 * Called when all commands are executed. |
| 747 */ | 898 */ |
| 748 void testComplete(CommandOutput lastCommandOutput) { | 899 void testComplete(CommandOutput lastCommandOutput) { |
| 900 var command = lastCommandOutput.command; | |
| 901 | |
| 749 if (timeoutTimer != null) { | 902 if (timeoutTimer != null) { |
| 750 timeoutTimer.cancel(); | 903 timeoutTimer.cancel(); |
| 751 } | 904 } |
| 752 if (lastCommandOutput.unexpectedOutput | 905 if (lastCommandOutput.unexpectedOutput |
| 753 && testCase.configuration['verbose'] != null | 906 && testCase.configuration['verbose'] != null |
| 754 && testCase.configuration['verbose']) { | 907 && testCase.configuration['verbose']) { |
| 755 print(testCase.displayName); | 908 print(testCase.displayName); |
| 756 for (var line in lastCommandOutput.stderr) print(line); | 909 print(''); |
|
ricow1
2012/12/17 14:20:54
I think the earlier print might have been here for
kustermann
2012/12/17 16:11:18
Done.
| |
| 757 for (var line in lastCommandOutput.stdout) print(line); | 910 if (notifications.length > 0) { |
| 911 print("Notifications:"); | |
| 912 for (var line in notifications) { | |
| 913 print(notifications); | |
| 914 } | |
| 915 print(''); | |
| 916 } | |
| 758 } | 917 } |
| 759 if (allowRetries && testCase.usesWebDriver | 918 if (allowRetries && testCase.usesWebDriver |
| 760 && lastCommandOutput.unexpectedOutput | 919 && lastCommandOutput.unexpectedOutput |
| 761 && (testCase as BrowserTestCase).numRetries > 0) { | 920 && (testCase as BrowserTestCase).numRetries > 0) { |
| 762 // Selenium tests can be flaky. Try rerunning. | 921 // Selenium tests can be flaky. Try rerunning. |
| 763 lastCommandOutput.requestRetry = true; | 922 lastCommandOutput.requestRetry = true; |
| 764 } | 923 } |
| 765 if (lastCommandOutput.requestRetry) { | 924 if (lastCommandOutput.requestRetry) { |
| 766 lastCommandOutput.requestRetry = false; | 925 lastCommandOutput.requestRetry = false; |
| 767 this.timedOut = false; | 926 this.timedOut = false; |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 789 if (timedOut) { | 948 if (timedOut) { |
| 790 // Non-webdriver test timed out before it could complete. Webdriver tests | 949 // Non-webdriver test timed out before it could complete. Webdriver tests |
| 791 // run their own timeouts by timing from the launch of the browser (which | 950 // run their own timeouts by timing from the launch of the browser (which |
| 792 // could be delayed). | 951 // could be delayed). |
| 793 testComplete(createCommandOutput(command, 0, true)); | 952 testComplete(createCommandOutput(command, 0, true)); |
| 794 } else if (currentStep == totalSteps) { | 953 } else if (currentStep == totalSteps) { |
| 795 // Done with all test commands. | 954 // Done with all test commands. |
| 796 testComplete(createCommandOutput(command, exitCode, false)); | 955 testComplete(createCommandOutput(command, exitCode, false)); |
| 797 } else if (exitCode != 0) { | 956 } else if (exitCode != 0) { |
| 798 // One of the steps failed. | 957 // One of the steps failed. |
| 799 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n'); | 958 notifications.add('test.dart: Compilation failed$suffix, ' |
| 959 'exit code $exitCode\n'); | |
| 800 testComplete(createCommandOutput(command, exitCode, true)); | 960 testComplete(createCommandOutput(command, exitCode, true)); |
| 801 } else { | 961 } else { |
| 802 createCommandOutput(command, exitCode, true); | 962 createCommandOutput(command, exitCode, true); |
| 803 // One compilation step successfully completed, move on to the | 963 // One compilation step successfully completed, move on to the |
| 804 // next step. | 964 // next step. |
| 805 stderr.add('test.dart: Compilation finished $suffix\n'); | 965 notifications.add('test.dart: Compilation finished $suffix\n\n'); |
| 806 stdout.add('test.dart: Compilation finished $suffix\n'); | |
| 807 if (currentStep == totalSteps - 1 && testCase.usesWebDriver && | 966 if (currentStep == totalSteps - 1 && testCase.usesWebDriver && |
| 808 !testCase.configuration['noBatch']) { | 967 !testCase.configuration['noBatch']) { |
| 809 // Note: processQueue will always be non-null for runtime == ie9, ie10, | 968 // Note: processQueue will always be non-null for runtime == ie9, ie10, |
| 810 // ff, safari, chrome, opera. (It is only null for runtime == vm) | 969 // ff, safari, chrome, opera. (It is only null for runtime == vm) |
| 811 // This RunningProcess object is done, and hands over control to | 970 // This RunningProcess object is done, and hands over control to |
| 812 // BatchRunner.startTest(), which handles reporting, etc. | 971 // BatchRunner.startTest(), which handles reporting, etc. |
| 813 if (timeoutTimer != null) { | 972 if (timeoutTimer != null) { |
| 814 timeoutTimer.cancel(); | 973 timeoutTimer.cancel(); |
| 815 } | 974 } |
| 816 processQueue._getBatchRunner(testCase).startTest(testCase); | 975 processQueue._getBatchRunner(testCase).startTest(testCase); |
| 817 } else { | 976 } else { |
| 818 runCommand(testCase.commands[currentStep++], commandComplete); | 977 runCommand(testCase.commands[currentStep++], commandComplete); |
| 819 } | 978 } |
| 820 } | 979 } |
| 821 } | 980 } |
| 822 | 981 |
| 823 /** | 982 /** |
| 824 * Called for all executed commands. | 983 * Called for all executed commands. |
| 825 */ | 984 */ |
| 826 CommandOutput createCommandOutput(Command command, | 985 CommandOutput createCommandOutput(Command command, |
| 827 int exitCode, | 986 int exitCode, |
| 828 bool incomplete) { | 987 bool incomplete) { |
| 988 // FIXME(kustermann): should we also include this.notifications ?? | |
| 829 var commandOutput = new CommandOutput.fromCase( | 989 var commandOutput = new CommandOutput.fromCase( |
| 830 testCase, | 990 testCase, |
| 831 command, | 991 command, |
| 832 exitCode, | 992 exitCode, |
| 833 incomplete, | 993 incomplete, |
| 834 timedOut, | 994 timedOut, |
| 835 stdout, | 995 stdout, |
| 836 stderr, | 996 stderr, |
| 837 new Date.now().difference(startTime), | 997 new Date.now().difference(startTime), |
| 838 compilationSkipped); | 998 compilationSkipped); |
| 839 resetLocalOutputInformation(); | 999 resetLocalOutputInformation(); |
| 840 return commandOutput; | 1000 return commandOutput; |
| 841 } | 1001 } |
| 842 | 1002 |
| 843 void resetLocalOutputInformation() { | 1003 void resetLocalOutputInformation() { |
| 844 stdout = new List<String>(); | 1004 stdout = new List<int>(); |
| 845 stderr = new List<String>(); | 1005 stderr = new List<int>(); |
| 1006 notifications = new List<String>(); | |
| 846 compilationSkipped = false; | 1007 compilationSkipped = false; |
| 847 } | 1008 } |
| 848 | 1009 |
| 849 VoidFunction makeReadHandler(StringInputStream source, | 1010 void drainStream(InputStream source, List<int> destination) { |
| 850 List<String> destination) { | 1011 void onDataHandler () { |
| 851 void handler () { | 1012 if (source.closed) { |
| 852 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. | 1013 return; // TODO(whesse): Remove when bug is fixed. |
| 853 var line = source.readLine(); | 1014 } |
| 854 while (null != line) { | 1015 var data = source.read(); |
| 855 destination.add(line); | 1016 while (data != null) { |
| 856 line = source.readLine(); | 1017 destination.addAll(data); |
| 1018 data = source.read(); | |
| 857 } | 1019 } |
| 858 } | 1020 } |
| 859 return handler; | 1021 source.onData = onDataHandler; |
| 1022 source.onClosed = onDataHandler; | |
| 860 } | 1023 } |
| 861 | 1024 |
| 862 void start() { | 1025 void start() { |
| 863 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); | 1026 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); |
| 864 resetLocalOutputInformation(); | 1027 resetLocalOutputInformation(); |
| 865 currentStep = 0; | 1028 currentStep = 0; |
| 866 startTime = new Date.now(); | 1029 startTime = new Date.now(); |
| 867 runCommand(testCase.commands[currentStep++], commandComplete); | 1030 runCommand(testCase.commands[currentStep++], commandComplete); |
| 868 } | 1031 } |
| 869 | 1032 |
| 870 void runCommand(Command command, void commandCompleteHandler(Command, int)) { | 1033 void runCommand(Command command, void commandCompleteHandler(Command, int)) { |
| 871 void processExitHandler(int returnCode) { | 1034 void processExitHandler(int returnCode) { |
| 872 commandCompleteHandler(command, returnCode); | 1035 commandCompleteHandler(command, returnCode); |
| 873 } | 1036 } |
| 874 | 1037 |
| 875 command.outputIsUpToDate.then((bool isUpToDate) { | 1038 command.outputIsUpToDate.then((bool isUpToDate) { |
| 876 if (isUpToDate) { | 1039 if (isUpToDate) { |
| 877 stdout.add("Skipped compilation because the old output is " | 1040 notifications.add("Skipped compilation because the old output is " |
| 878 "still up to date!"); | 1041 "still up to date!"); |
| 879 compilationSkipped = true; | 1042 compilationSkipped = true; |
| 880 commandComplete(command, 0); | 1043 commandComplete(command, 0); |
| 881 } else { | 1044 } else { |
| 882 ProcessOptions options = new ProcessOptions(); | 1045 ProcessOptions options = new ProcessOptions(); |
| 883 options.environment = | 1046 if (command.environment != null) { |
| 884 new Map<String, String>.from(Platform.environment); | 1047 options.environment = |
| 1048 new Map<String, String>.from(command.environment); | |
| 1049 } else { | |
| 1050 options.environment = | |
| 1051 new Map<String, String>.from(Platform.environment); | |
| 1052 } | |
| 1053 | |
| 885 options.environment['DART_CONFIGURATION'] = | 1054 options.environment['DART_CONFIGURATION'] = |
| 886 TestUtils.configurationDir(testCase.configuration); | 1055 TestUtils.configurationDir(testCase.configuration); |
| 887 Future processFuture = Process.start(command.executable, | 1056 Future processFuture = Process.start(command.executable, |
| 888 command.arguments, | 1057 command.arguments, |
| 889 options); | 1058 options); |
| 890 processFuture.then((Process p) { | 1059 processFuture.then((Process p) { |
| 891 process = p; | 1060 process = p; |
| 892 process.onExit = processExitHandler; | 1061 process.onExit = processExitHandler; |
| 893 var stdoutStringStream = new StringInputStream(process.stdout); | 1062 drainStream(process.stdout, stdout); |
| 894 var stderrStringStream = new StringInputStream(process.stderr); | 1063 drainStream(process.stderr, stderr); |
| 895 stdoutStringStream.onLine = | |
| 896 makeReadHandler(stdoutStringStream, stdout); | |
| 897 stderrStringStream.onLine = | |
| 898 makeReadHandler(stderrStringStream, stderr); | |
| 899 if (timeoutTimer == null) { | 1064 if (timeoutTimer == null) { |
| 900 // Create one timeout timer when starting test case, remove it at | 1065 // Create one timeout timer when starting test case, remove it at |
| 901 // the end. | 1066 // the end. |
| 902 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); | 1067 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); |
| 903 } | 1068 } |
| 904 // If the timeout fired in between two commands, kill the just | 1069 // If the timeout fired in between two commands, kill the just |
| 905 // started process immediately. | 1070 // started process immediately. |
| 906 if (timedOut) safeKill(process); | 1071 if (timedOut) safeKill(process); |
| 907 }); | 1072 }); |
| 908 processFuture.handleException((e) { | 1073 processFuture.handleException((e) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 944 class BatchRunnerProcess { | 1109 class BatchRunnerProcess { |
| 945 Command _command; | 1110 Command _command; |
| 946 String _executable; | 1111 String _executable; |
| 947 List<String> _batchArguments; | 1112 List<String> _batchArguments; |
| 948 | 1113 |
| 949 Process _process; | 1114 Process _process; |
| 950 StringInputStream _stdoutStream; | 1115 StringInputStream _stdoutStream; |
| 951 StringInputStream _stderrStream; | 1116 StringInputStream _stderrStream; |
| 952 | 1117 |
| 953 TestCase _currentTest; | 1118 TestCase _currentTest; |
| 954 List<String> _testStdout; | 1119 List<int> _testStdout; |
| 955 List<String> _testStderr; | 1120 List<int> _testStderr; |
| 956 String _status; | 1121 String _status; |
| 957 bool _stdoutDrained = false; | 1122 bool _stdoutDrained = false; |
| 958 bool _stderrDrained = false; | 1123 bool _stderrDrained = false; |
| 959 MutableValue<bool> _ignoreStreams; | 1124 MutableValue<bool> _ignoreStreams; |
| 960 Date _startTime; | 1125 Date _startTime; |
| 961 Timer _timer; | 1126 Timer _timer; |
| 962 | 1127 |
| 963 bool _isWebDriver; | 1128 bool _isWebDriver; |
| 964 | 1129 |
| 965 BatchRunnerProcess(TestCase testCase) { | 1130 BatchRunnerProcess(TestCase testCase) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1022 } | 1187 } |
| 1023 | 1188 |
| 1024 void doStartTest(TestCase testCase) { | 1189 void doStartTest(TestCase testCase) { |
| 1025 _startTime = new Date.now(); | 1190 _startTime = new Date.now(); |
| 1026 _testStdout = []; | 1191 _testStdout = []; |
| 1027 _testStderr = []; | 1192 _testStderr = []; |
| 1028 _status = null; | 1193 _status = null; |
| 1029 _stdoutDrained = false; | 1194 _stdoutDrained = false; |
| 1030 _stderrDrained = false; | 1195 _stderrDrained = false; |
| 1031 _ignoreStreams = new MutableValue<bool>(false); // Captured by closures. | 1196 _ignoreStreams = new MutableValue<bool>(false); // Captured by closures. |
| 1032 _stdoutStream.onLine = _readStdout(_stdoutStream, _testStdout); | 1197 _readStdout(_stdoutStream, _testStdout); |
| 1033 _stderrStream.onLine = _readStderr(_stderrStream, _testStderr); | 1198 _readStderr(_stderrStream, _testStderr); |
| 1034 _timer = new Timer(testCase.timeout * 1000, _timeoutHandler); | 1199 _timer = new Timer(testCase.timeout * 1000, _timeoutHandler); |
| 1200 | |
| 1201 if (testCase.commands.last.environment != null) { | |
| 1202 print("Warning: command.environment != null, but we don't support custom " | |
| 1203 "environments for batch runner tests!"); | |
| 1204 } | |
| 1205 | |
| 1035 var line = _createArgumentsLine(testCase.batchTestArguments); | 1206 var line = _createArgumentsLine(testCase.batchTestArguments); |
| 1036 _process.stdin.onError = (err) { | 1207 _process.stdin.onError = (err) { |
| 1037 print('Error on batch runner input stream stdin'); | 1208 print('Error on batch runner input stream stdin'); |
| 1038 print(' Input line: $line'); | 1209 print(' Input line: $line'); |
| 1039 print(' Previous test\'s status: $_status'); | 1210 print(' Previous test\'s status: $_status'); |
| 1040 print(' Error: $err'); | 1211 print(' Error: $err'); |
| 1041 throw err; | 1212 throw err; |
| 1042 }; | 1213 }; |
| 1043 _process.stdin.write(line.charCodes); | 1214 _process.stdin.write(line.charCodes); |
| 1044 } | 1215 } |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 1074 // Move on when both stdout and stderr has been drained. | 1245 // Move on when both stdout and stderr has been drained. |
| 1075 if (_stdoutDrained) _reportResult(); | 1246 if (_stdoutDrained) _reportResult(); |
| 1076 } | 1247 } |
| 1077 | 1248 |
| 1078 void _stdoutDone() { | 1249 void _stdoutDone() { |
| 1079 _stdoutDrained = true; | 1250 _stdoutDrained = true; |
| 1080 // Move on when both stdout and stderr has been drained. | 1251 // Move on when both stdout and stderr has been drained. |
| 1081 if (_stderrDrained) _reportResult(); | 1252 if (_stderrDrained) _reportResult(); |
| 1082 } | 1253 } |
| 1083 | 1254 |
| 1084 VoidFunction _readStdout(StringInputStream stream, List<String> buffer) { | 1255 void _readStdout(StringInputStream stream, List<int> buffer) { |
| 1085 var ignoreStreams = _ignoreStreams; // Capture this mutable object. | 1256 var ignoreStreams = _ignoreStreams; // Capture this mutable object. |
| 1086 void reader() { | 1257 void onLineHandler() { |
| 1087 if (ignoreStreams.value) { | 1258 if (ignoreStreams.value) { |
| 1088 while (stream.readLine() != null) { | 1259 while (stream.readLine() != null) { |
| 1089 // Do nothing. | 1260 // Do nothing. |
| 1090 } | 1261 } |
| 1091 return; | 1262 return; |
| 1092 } | 1263 } |
| 1093 // Otherwise, process output and call _reportResult() when done. | 1264 // Otherwise, process output and call _reportResult() when done. |
| 1094 var line = stream.readLine(); | 1265 var line = stream.readLine(); |
| 1095 while (line != null) { | 1266 while (line != null) { |
| 1096 if (line.startsWith('>>> TEST')) { | 1267 if (line.startsWith('>>> TEST')) { |
| 1097 _status = line; | 1268 _status = line; |
| 1098 } else if (line.startsWith('>>> BATCH START')) { | 1269 } else if (line.startsWith('>>> BATCH START')) { |
| 1099 // ignore | 1270 // ignore |
| 1100 } else if (line.startsWith('>>> ')) { | 1271 } else if (line.startsWith('>>> ')) { |
| 1101 throw new Exception('Unexpected command from dartc batch runner.'); | 1272 throw new Exception('Unexpected command from dartc batch runner.'); |
| 1102 } else { | 1273 } else { |
| 1103 buffer.add(line); | 1274 buffer.addAll("$line\n".charCodes); |
| 1104 } | 1275 } |
| 1105 line = stream.readLine(); | 1276 line = stream.readLine(); |
| 1106 } | 1277 } |
| 1107 if (_status != null) { | 1278 if (_status != null) { |
| 1108 _timer.cancel(); | 1279 _timer.cancel(); |
| 1109 _stdoutDone(); | 1280 _stdoutDone(); |
| 1110 } | 1281 } |
| 1111 } | 1282 } |
| 1112 return reader; | 1283 stream.onLine = onLineHandler; |
| 1113 } | 1284 } |
| 1114 | 1285 |
| 1115 VoidFunction _readStderr(StringInputStream stream, List<String> buffer) { | 1286 void _readStderr(StringInputStream stream, List<int> buffer) { |
| 1116 var ignoreStreams = _ignoreStreams; // Capture this mutable object. | 1287 var ignoreStreams = _ignoreStreams; // Capture this mutable object. |
| 1117 void reader() { | 1288 void onLineHandler() { |
| 1118 if (ignoreStreams.value) { | 1289 if (ignoreStreams.value) { |
| 1119 while (stream.readLine() != null) { | 1290 while (stream.readLine() != null) { |
| 1120 // Do nothing. | 1291 // Do nothing. |
| 1121 } | 1292 } |
| 1122 return; | 1293 return; |
| 1123 } | 1294 } |
| 1124 // Otherwise, process output and call _reportResult() when done. | 1295 // Otherwise, process output and call _reportResult() when done. |
| 1125 var line = stream.readLine(); | 1296 var line = stream.readLine(); |
| 1126 while (line != null) { | 1297 while (line != null) { |
| 1127 if (line.startsWith('>>> EOF STDERR')) { | 1298 if (line.startsWith('>>> EOF STDERR')) { |
| 1128 _stderrDone(); | 1299 _stderrDone(); |
| 1129 } else { | 1300 } else { |
| 1130 buffer.add(line); | 1301 buffer.addAll("$line\n".charCodes); |
| 1131 } | 1302 } |
| 1132 line = stream.readLine(); | 1303 line = stream.readLine(); |
| 1133 } | 1304 } |
| 1134 } | 1305 } |
| 1135 return reader; | 1306 stream.onLine = onLineHandler; |
| 1136 } | 1307 } |
| 1137 | 1308 |
| 1138 ExitCodeEvent makeExitHandler(String status) { | 1309 ExitCodeEvent makeExitHandler(String status) { |
| 1139 void handler(int exitCode) { | 1310 void handler(int exitCode) { |
| 1140 if (active) { | 1311 if (active) { |
| 1141 if (_timer != null) _timer.cancel(); | 1312 if (_timer != null) _timer.cancel(); |
| 1142 _status = status; | 1313 _status = status; |
| 1143 // Read current content of streams, ignore any later output. | 1314 // Read current content of streams, ignore any later output. |
| 1144 _ignoreStreams.value = true; | 1315 _ignoreStreams.value = true; |
| 1145 var line = _stdoutStream.readLine(); | 1316 var line = _stdoutStream.readLine(); |
| (...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1521 // the developer doesn't waste his or her time trying to fix a bunch of | 1692 // the developer doesn't waste his or her time trying to fix a bunch of |
| 1522 // tests that appear to be broken but were actually just flakes that | 1693 // tests that appear to be broken but were actually just flakes that |
| 1523 // didn't get retried because there had already been one failure. | 1694 // didn't get retried because there had already been one failure. |
| 1524 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; | 1695 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 1525 new RunningProcess(test, allowRetry, this).start(); | 1696 new RunningProcess(test, allowRetry, this).start(); |
| 1526 } | 1697 } |
| 1527 _numProcesses++; | 1698 _numProcesses++; |
| 1528 } | 1699 } |
| 1529 } | 1700 } |
| 1530 } | 1701 } |
| OLD | NEW |