| 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. |
| 11 */ | 11 */ |
| 12 library test_runner; | 12 library test_runner; |
| 13 | 13 |
| 14 import "dart:io"; | 14 import "dart:io"; |
| 15 import "dart:isolate"; | 15 import "dart:isolate"; |
| 16 import "dart:uri"; | 16 import "dart:uri"; |
| 17 import "status_file_parser.dart"; | 17 import "status_file_parser.dart"; |
| 18 import "test_progress.dart"; | 18 import "test_progress.dart"; |
| 19 import "test_suite.dart"; | 19 import "test_suite.dart"; |
| 20 | 20 |
| 21 const int NO_TIMEOUT = 0; | 21 const int NO_TIMEOUT = 0; |
| 22 const int SLOW_TIMEOUT_MULTIPLIER = 4; | 22 const int SLOW_TIMEOUT_MULTIPLIER = 4; |
| 23 | 23 |
| 24 typedef void TestCaseEvent(TestCase testCase); | 24 typedef void TestCaseEvent(TestCase testCase); |
| 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 |
| 29 /** |
| 30 * [areByteArraysEqual] compares a range of bytes from [buffer1] with a |
| 31 * range of bytes from [buffer2]. |
| 32 * |
| 33 * Returns [true] if the [count] bytes in [buffer1] (starting at |
| 34 * [offset1]) match the [count] bytes in [buffer2] (starting at |
| 35 * [offset2]). |
| 36 * Otherwise [false] is returned. |
| 37 */ |
| 38 bool areByteArraysEqual(List<int> buffer1, int offset1, |
| 39 List<int> buffer2, int offset2, |
| 40 int count) { |
| 41 if ((offset1 + count) > buffer1.length || |
| 42 (offset2 + count) > buffer2.length) { |
| 43 return false; |
| 44 } |
| 45 |
| 46 for (var i = 0; i < count; i++) { |
| 47 if (buffer1[offset1 + i] != buffer2[offset2 + i]) { |
| 48 return false; |
| 49 } |
| 50 } |
| 51 return true; |
| 52 } |
| 53 |
| 54 /** |
| 55 * [findBytes] searches for [pattern] in [data] beginning at [startPos]. |
| 56 * |
| 57 * Returns [true] if [pattern] was found in [data]. |
| 58 * Otherwise [false] is returned. |
| 59 */ |
| 60 int findBytes(List<int> data, List<int> pattern, [int startPos=0]) { |
| 61 // TODO(kustermann): Use one of the fast string-matching algorithms! |
| 62 for (int i=startPos; i < (data.length-pattern.length); i++) { |
| 63 bool found = true; |
| 64 for (int j=0; j<pattern.length; j++) { |
| 65 if (data[i+j] != pattern[j]) { |
| 66 found = false; |
| 67 } |
| 68 } |
| 69 if (found) { |
| 70 return i; |
| 71 } |
| 72 } |
| 73 return -1; |
| 74 } |
| 75 |
| 76 |
| 28 /** A command executed as a step in a test case. */ | 77 /** A command executed as a step in a test case. */ |
| 29 class Command { | 78 class Command { |
| 30 /** Path to the executable of this command. */ | 79 /** Path to the executable of this command. */ |
| 31 String executable; | 80 String executable; |
| 32 | 81 |
| 33 /** Command line arguments to the executable. */ | 82 /** Command line arguments to the executable. */ |
| 34 List<String> arguments; | 83 List<String> arguments; |
| 84 |
| 85 /** Environment for the command */ |
| 86 Map<String,String> environment; |
| 35 | 87 |
| 36 /** The actual command line that will be executed. */ | 88 /** The actual command line that will be executed. */ |
| 37 String commandLine; | 89 String commandLine; |
| 38 | 90 |
| 39 Command(this.executable, this.arguments) { | 91 Command(this.executable, this.arguments, [this.environment = null]) { |
| 40 if (Platform.operatingSystem == 'windows') { | 92 if (Platform.operatingSystem == 'windows') { |
| 41 // Windows can't handle the first command if it is a .bat file or the like | 93 // Windows can't handle the first command if it is a .bat file or the like |
| 42 // with the slashes going the other direction. | 94 // with the slashes going the other direction. |
| 43 // TODO(efortuna): Remove this when fixed (Issue 1306). | 95 // TODO(efortuna): Remove this when fixed (Issue 1306). |
| 44 executable = executable.replaceAll('/', '\\'); | 96 executable = executable.replaceAll('/', '\\'); |
| 45 } | 97 } |
| 46 commandLine = "$executable ${Strings.join(arguments, ' ')}"; | 98 commandLine = "$executable ${Strings.join(arguments, ' ')}"; |
| 47 } | 99 } |
| 48 | 100 |
| 49 String toString() => commandLine; | 101 String toString() => commandLine; |
| 50 | 102 |
| 51 Future<bool> get outputIsUpToDate => new Future.immediate(false); | 103 Future<bool> get outputIsUpToDate => new Future.immediate(false); |
| 104 Path get expectedOutputFile => null; |
| 105 bool get isPixelTest => false; |
| 52 } | 106 } |
| 53 | 107 |
| 54 class CompilationCommand extends Command { | 108 class CompilationCommand extends Command { |
| 55 String _outputFile; | 109 String _outputFile; |
| 56 bool _neverSkipCompilation; | 110 bool _neverSkipCompilation; |
| 57 List<Uri> _bootstrapDependencies; | 111 List<Uri> _bootstrapDependencies; |
| 58 | 112 |
| 59 CompilationCommand(this._outputFile, | 113 CompilationCommand(this._outputFile, |
| 60 this._neverSkipCompilation, | 114 this._neverSkipCompilation, |
| 61 this._bootstrapDependencies, | 115 this._bootstrapDependencies, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 98 } | 152 } |
| 99 } | 153 } |
| 100 return true; | 154 return true; |
| 101 } | 155 } |
| 102 } | 156 } |
| 103 return false; | 157 return false; |
| 104 }); | 158 }); |
| 105 } | 159 } |
| 106 } | 160 } |
| 107 | 161 |
| 162 class DumpRenderTreeCommand extends Command { |
| 163 /** |
| 164 * If [expectedOutputPath] is set, the output of DumpRenderTree is compared |
| 165 * with the content of [expectedOutputPath]. |
| 166 * This is used for example for pixel tests, where [expectedOutputPath] points |
| 167 * to a *png file. |
| 168 */ |
| 169 Path expectedOutputPath; |
| 170 |
| 171 DumpRenderTreeCommand(String executable, |
| 172 String htmlFile, |
| 173 List<String> options, |
| 174 List<String> dartFlags, |
| 175 Uri packageRootUri, |
| 176 Path this.expectedOutputPath) |
| 177 : super(executable, |
| 178 _getArguments(options, htmlFile), |
| 179 _getEnvironment(dartFlags, packageRootUri)); |
| 180 |
| 181 static Map _getEnvironment(List<String> dartFlags, Uri packageRootUri) { |
| 182 var needDartFlags = dartFlags != null && dartFlags.length > 0; |
| 183 var needDartPackageRoot = packageRootUri != null; |
| 184 |
| 185 var env = null; |
| 186 if (needDartFlags || needDartPackageRoot) { |
| 187 env = new Map.from(Platform.environment); |
| 188 if (needDartFlags) { |
| 189 env['DART_FLAGS'] = Strings.join(dartFlags, " "); |
| 190 } |
| 191 if (needDartPackageRoot) { |
| 192 env['DART_PACKAGE_ROOT'] = packageRootUri.toString(); |
| 193 } |
| 194 } |
| 195 |
| 196 return env; |
| 197 } |
| 198 |
| 199 static List<String> _getArguments(List<String> options, String htmlFile) { |
| 200 var arguments = new List.from(options); |
| 201 arguments.add(htmlFile); |
| 202 return arguments; |
| 203 } |
| 204 |
| 205 Path get expectedOutputFile => expectedOutputPath; |
| 206 bool get isPixelTest => (expectedOutputFile != null && |
| 207 expectedOutputFile.filename.endsWith(".png")); |
| 208 } |
| 209 |
| 210 |
| 108 /** | 211 /** |
| 109 * TestCase contains all the information needed to run a test and evaluate | 212 * TestCase contains all the information needed to run a test and evaluate |
| 110 * its output. Running a test involves starting a separate process, with | 213 * its output. Running a test involves starting a separate process, with |
| 111 * the executable and arguments given by the TestCase, and recording its | 214 * the executable and arguments given by the TestCase, and recording its |
| 112 * stdout and stderr output streams, and its exit code. TestCase only | 215 * stdout and stderr output streams, and its exit code. TestCase only |
| 113 * contains static information about the test; actually running the test is | 216 * contains static information about the test; actually running the test is |
| 114 * performed by [ProcessQueue] using a [RunningProcess] object. | 217 * performed by [ProcessQueue] using a [RunningProcess] object. |
| 115 * | 218 * |
| 116 * The output information is stored in a [CommandOutput] instance contained | 219 * The output information is stored in a [CommandOutput] instance contained |
| 117 * in TestCase.commandOutputs. The last CommandOutput instance is responsible | 220 * 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, | 413 * 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 | 414 * and the time the process took to run. It also contains a pointer to the |
| 312 * [TestCase] this is the output of. | 415 * [TestCase] this is the output of. |
| 313 */ | 416 */ |
| 314 abstract class CommandOutput { | 417 abstract class CommandOutput { |
| 315 factory CommandOutput.fromCase(TestCase testCase, | 418 factory CommandOutput.fromCase(TestCase testCase, |
| 316 Command command, | 419 Command command, |
| 317 int exitCode, | 420 int exitCode, |
| 318 bool incomplete, | 421 bool incomplete, |
| 319 bool timedOut, | 422 bool timedOut, |
| 320 List<String> stdout, | 423 List<int> stdout, |
| 321 List<String> stderr, | 424 List<int> stderr, |
| 322 Duration time, | 425 Duration time, |
| 323 bool compilationSkipped) { | 426 bool compilationSkipped) { |
| 324 return new CommandOutputImpl.fromCase(testCase, | 427 return new CommandOutputImpl.fromCase(testCase, |
| 325 command, | 428 command, |
| 326 exitCode, | 429 exitCode, |
| 327 incomplete, | 430 incomplete, |
| 328 timedOut, | 431 timedOut, |
| 329 stdout, | 432 stdout, |
| 330 stderr, | 433 stderr, |
| 331 time, | 434 time, |
| 332 compilationSkipped); | 435 compilationSkipped); |
| 333 } | 436 } |
| 334 | 437 |
| 438 Command get command; |
| 439 |
| 335 bool get incomplete; | 440 bool get incomplete; |
| 336 | 441 |
| 337 String get result; | 442 String get result; |
| 338 | 443 |
| 339 bool get unexpectedOutput; | 444 bool get unexpectedOutput; |
| 340 | 445 |
| 341 bool get hasCrashed; | 446 bool get hasCrashed; |
| 342 | 447 |
| 343 bool get hasTimedOut; | 448 bool get hasTimedOut; |
| 344 | 449 |
| 345 bool get didFail; | 450 bool get didFail; |
| 346 | 451 |
| 347 bool requestRetry; | 452 bool requestRetry; |
| 348 | 453 |
| 349 Duration get time; | 454 Duration get time; |
| 350 | 455 |
| 351 int get exitCode; | 456 int get exitCode; |
| 352 | 457 |
| 353 List<String> get stdout; | 458 List<int> get stdout; |
| 354 | 459 |
| 355 List<String> get stderr; | 460 List<int> get stderr; |
| 356 | 461 |
| 357 List<String> get diagnostics; | 462 List<String> get diagnostics; |
| 358 | 463 |
| 359 bool get compilationSkipped; | 464 bool get compilationSkipped; |
| 360 } | 465 } |
| 361 | 466 |
| 362 class CommandOutputImpl implements CommandOutput { | 467 class CommandOutputImpl implements CommandOutput { |
| 468 Command command; |
| 363 TestCase testCase; | 469 TestCase testCase; |
| 364 int exitCode; | 470 int exitCode; |
| 365 | 471 |
| 366 /// Records if all commands were run, true if they weren't. | 472 /// Records if all commands were run, true if they weren't. |
| 367 final bool incomplete; | 473 final bool incomplete; |
| 368 | 474 |
| 369 bool timedOut; | 475 bool timedOut; |
| 370 bool failed = false; | 476 bool failed = false; |
| 371 List<String> stdout; | 477 List<int> stdout; |
| 372 List<String> stderr; | 478 List<int> stderr; |
| 373 Duration time; | 479 Duration time; |
| 374 List<String> diagnostics; | 480 List<String> diagnostics; |
| 375 bool compilationSkipped; | 481 bool compilationSkipped; |
| 376 | 482 |
| 377 /** | 483 /** |
| 378 * A flag to indicate we have already printed a warning about ignoring the VM | 484 * 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. | 485 * crash, to limit the amount of output produced per test. |
| 380 */ | 486 */ |
| 381 bool alreadyPrintedWarning = false; | 487 bool alreadyPrintedWarning = false; |
| 382 | 488 |
| 383 /** | 489 /** |
| 384 * Set to true if we encounter a condition in the output that indicates we | 490 * Set to true if we encounter a condition in the output that indicates we |
| 385 * need to rerun this test. | 491 * need to rerun this test. |
| 386 */ | 492 */ |
| 387 bool requestRetry = false; | 493 bool requestRetry = false; |
| 388 | 494 |
| 389 // Don't call this constructor, call CommandOutput.fromCase() to | 495 // Don't call this constructor, call CommandOutput.fromCase() to |
| 390 // get a new TestOutput instance. | 496 // get a new TestOutput instance. |
| 391 CommandOutputImpl(TestCase this.testCase, | 497 CommandOutputImpl(TestCase this.testCase, |
| 392 Command command, | 498 Command this.command, |
| 393 int this.exitCode, | 499 int this.exitCode, |
| 394 bool this.incomplete, | 500 bool this.incomplete, |
| 395 bool this.timedOut, | 501 bool this.timedOut, |
| 396 List<String> this.stdout, | 502 List<int> this.stdout, |
| 397 List<String> this.stderr, | 503 List<int> this.stderr, |
| 398 Duration this.time, | 504 Duration this.time, |
| 399 bool this.compilationSkipped) { | 505 bool this.compilationSkipped) { |
| 400 testCase.commandOutputs[command] = this; | 506 testCase.commandOutputs[command] = this; |
| 401 diagnostics = []; | 507 diagnostics = []; |
| 402 } | 508 } |
| 403 factory CommandOutputImpl.fromCase(TestCase testCase, | 509 factory CommandOutputImpl.fromCase(TestCase testCase, |
| 404 Command command, | 510 Command command, |
| 405 int exitCode, | 511 int exitCode, |
| 406 bool incomplete, | 512 bool incomplete, |
| 407 bool timedOut, | 513 bool timedOut, |
| 408 List<String> stdout, | 514 List<int> stdout, |
| 409 List<String> stderr, | 515 List<int> stderr, |
| 410 Duration time, | 516 Duration time, |
| 411 bool compilationSkipped) { | 517 bool compilationSkipped) { |
| 412 if (testCase is BrowserTestCase) { | 518 if (testCase is BrowserTestCase) { |
| 413 return new BrowserCommandOutputImpl(testCase, | 519 return new BrowserCommandOutputImpl(testCase, |
| 414 command, | 520 command, |
| 415 exitCode, | 521 exitCode, |
| 416 incomplete, | 522 incomplete, |
| 417 timedOut, | 523 timedOut, |
| 418 stdout, | 524 stdout, |
| 419 stderr, | 525 stderr, |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 492 command, | 598 command, |
| 493 exitCode, | 599 exitCode, |
| 494 incomplete, | 600 incomplete, |
| 495 timedOut, | 601 timedOut, |
| 496 stdout, | 602 stdout, |
| 497 stderr, | 603 stderr, |
| 498 time, | 604 time, |
| 499 compilationSkipped); | 605 compilationSkipped); |
| 500 | 606 |
| 501 bool get didFail { | 607 bool get didFail { |
| 608 if (_failedBecauseOfMissingXDisplay) { |
| 609 return true; |
| 610 } |
| 611 |
| 612 if (command.expectedOutputFile != null) { |
| 613 // We are either doing a pixel test or a layout test with DumpRenderTree |
| 614 return _failedBecauseOfUnexpectedDRTOutput; |
| 615 } |
| 616 return _browserTestFailure; |
| 617 } |
| 618 |
| 619 bool get _failedBecauseOfMissingXDisplay { |
| 502 // Browser case: | 620 // Browser case: |
| 503 // If the browser test failed, it may have been because DumpRenderTree | 621 // 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 | 622 // 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, | 623 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, |
| 506 // so we have to do this check first. | 624 // so we have to do this check first. |
| 507 for (String line in super.stderr) { | 625 var stderrLines = new String.fromCharCodes(super.stderr).split("\n"); |
| 626 for (String line in stderrLines) { |
| 508 if (line.contains('Gtk-WARNING **: cannot open display: :99') || | 627 if (line.contains('Gtk-WARNING **: cannot open display: :99') || |
| 509 line.contains('Failed to run command. return code=1')) { | 628 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 | 629 // If we get the X server error, or DRT crashes with a core dump, retry |
| 511 // the test. | 630 // the test. |
| 512 if ((testCase as BrowserTestCase).numRetries > 0) { | 631 if ((testCase as BrowserTestCase).numRetries > 0) { |
| 513 requestRetry = true; | 632 requestRetry = true; |
| 514 } | 633 } |
| 515 return true; | 634 return true; |
| 516 } | 635 } |
| 517 } | 636 } |
| 637 return false; |
| 638 } |
| 518 | 639 |
| 640 bool get _failedBecauseOfUnexpectedDRTOutput { |
| 641 /* |
| 642 * The output of DumpRenderTree is different for pixel tests than for |
| 643 * layout tests. |
| 644 * |
| 645 * On a pixel test, the DRT output has the following format |
| 646 * ...... |
| 647 * ...... |
| 648 * Content-Length: ...\n |
| 649 * <*png data> |
| 650 * #EOF\n |
| 651 * So we need to get the byte-range of the png data first, before |
| 652 * comparing it with the content of the expected output file. |
| 653 * |
| 654 * On a layout tests, the DRT output is directly compared with the |
| 655 * content of the expected output. |
| 656 */ |
| 657 var stdout = testCase.commandOutputs[command].stdout; |
| 658 var file = new File.fromPath(command.expectedOutputFile); |
| 659 if (file.existsSync()) { |
| 660 var bytesContentLength = "Content-Length:".charCodes; |
| 661 var bytesNewLine = "\n".charCodes; |
| 662 var bytesEOF = "#EOF\n".charCodes; |
| 663 |
| 664 var expectedContent = file.readAsBytesSync(); |
| 665 if (command.isPixelTest) { |
| 666 var startOfContentLength = findBytes(stdout, bytesContentLength); |
| 667 if (startOfContentLength >= 0) { |
| 668 var newLineAfterContentLength = findBytes(stdout, |
| 669 bytesNewLine, |
| 670 startOfContentLength); |
| 671 if (newLineAfterContentLength > 0) { |
| 672 var startPosition = newLineAfterContentLength + |
| 673 bytesNewLine.length; |
| 674 var endPosition = stdout.length - bytesEOF.length; |
| 675 |
| 676 return !areByteArraysEqual(expectedContent, |
| 677 0, |
| 678 stdout, |
| 679 startPosition, |
| 680 endPosition - startPosition); |
| 681 } |
| 682 } |
| 683 return true; |
| 684 } else { |
| 685 return !areByteArraysEqual(expectedContent, 0, |
| 686 stdout, 0, |
| 687 stdout.length); |
| 688 } |
| 689 } |
| 690 return true; |
| 691 } |
| 692 |
| 693 bool get _browserTestFailure { |
| 519 // Browser tests fail unless stdout contains | 694 // Browser tests fail unless stdout contains |
| 520 // 'Content-Type: text/plain' followed by 'PASS'. | 695 // 'Content-Type: text/plain' followed by 'PASS'. |
| 521 bool has_content_type = false; | 696 bool has_content_type = false; |
| 522 for (String line in super.stdout) { | 697 var stdoutLines = new String.fromCharCodes(super.stdout).split("\n"); |
| 698 for (String line in stdoutLines) { |
| 523 switch (line) { | 699 switch (line) { |
| 524 case 'Content-Type: text/plain': | 700 case 'Content-Type: text/plain': |
| 525 has_content_type = true; | 701 has_content_type = true; |
| 526 break; | 702 break; |
| 527 | |
| 528 case 'PASS': | 703 case 'PASS': |
| 529 if (has_content_type) { | 704 if (has_content_type) { |
| 530 return (exitCode != 0 && !hasCrashed); | 705 return (exitCode != 0 && !hasCrashed); |
| 531 } | 706 } |
| 532 break; | 707 break; |
| 533 } | 708 } |
| 534 } | 709 } |
| 535 return true; | 710 return true; |
| 536 } | 711 } |
| 537 } | 712 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 575 return failResult; | 750 return failResult; |
| 576 } | 751 } |
| 577 | 752 |
| 578 bool _didFail() { | 753 bool _didFail() { |
| 579 if (hasCrashed) return false; | 754 if (hasCrashed) return false; |
| 580 | 755 |
| 581 List<String> errors = []; | 756 List<String> errors = []; |
| 582 List<String> staticWarnings = []; | 757 List<String> staticWarnings = []; |
| 583 | 758 |
| 584 // Read the returned list of errors and stuff them away. | 759 // Read the returned list of errors and stuff them away. |
| 585 for (String line in super.stderr) { | 760 var stderrLines = new String.fromCharCodes(super.stderr).split("\n"); |
| 761 for (String line in stderrLines) { |
| 586 if (line.length == 0) continue; | 762 if (line.length == 0) continue; |
| 587 List<String> fields = splitMachineError(line); | 763 List<String> fields = splitMachineError(line); |
| 588 if (fields[ERROR_LEVEL] == 'ERROR') { | 764 if (fields[ERROR_LEVEL] == 'ERROR') { |
| 589 errors.add(fields[FORMATTED_ERROR]); | 765 errors.add(fields[FORMATTED_ERROR]); |
| 590 } else if (fields[ERROR_LEVEL] == 'WARNING') { | 766 } else if (fields[ERROR_LEVEL] == 'WARNING') { |
| 591 // We only care about testing Static type warnings | 767 // We only care about testing Static type warnings |
| 592 // ignore all others | 768 // ignore all others |
| 593 if (fields[ERROR_TYPE] == 'STATIC_TYPE') { | 769 if (fields[ERROR_TYPE] == 'STATIC_TYPE') { |
| 594 staticWarnings.add(fields[FORMATTED_ERROR]); | 770 staticWarnings.add(fields[FORMATTED_ERROR]); |
| 595 } | 771 } |
| (...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 | 900 * the result; there are no pointers to it, so it should be available to |
| 725 * be garbage collected as soon as it is done. | 901 * be garbage collected as soon as it is done. |
| 726 */ | 902 */ |
| 727 class RunningProcess { | 903 class RunningProcess { |
| 728 ProcessQueue processQueue; | 904 ProcessQueue processQueue; |
| 729 Process process; | 905 Process process; |
| 730 TestCase testCase; | 906 TestCase testCase; |
| 731 bool timedOut = false; | 907 bool timedOut = false; |
| 732 Date startTime; | 908 Date startTime; |
| 733 Timer timeoutTimer; | 909 Timer timeoutTimer; |
| 734 List<String> stdout; | 910 List<int> stdout; |
| 735 List<String> stderr; | 911 List<int> stderr; |
| 912 List<String> notifications; |
| 736 bool compilationSkipped; | 913 bool compilationSkipped; |
| 737 bool allowRetries; | 914 bool allowRetries; |
| 738 | 915 |
| 739 /** Which command of [testCase.commands] is currently being executed. */ | 916 /** Which command of [testCase.commands] is currently being executed. */ |
| 740 int currentStep; | 917 int currentStep; |
| 741 | 918 |
| 742 RunningProcess(TestCase this.testCase, | 919 RunningProcess(TestCase this.testCase, |
| 743 [this.allowRetries = false, this.processQueue]); | 920 [this.allowRetries = false, this.processQueue]); |
| 744 | 921 |
| 745 /** | 922 /** |
| 746 * Called when all commands are executed. | 923 * Called when all commands are executed. |
| 747 */ | 924 */ |
| 748 void testComplete(CommandOutput lastCommandOutput) { | 925 void testComplete(CommandOutput lastCommandOutput) { |
| 926 var command = lastCommandOutput.command; |
| 927 |
| 749 if (timeoutTimer != null) { | 928 if (timeoutTimer != null) { |
| 750 timeoutTimer.cancel(); | 929 timeoutTimer.cancel(); |
| 751 } | 930 } |
| 752 if (lastCommandOutput.unexpectedOutput | 931 if (lastCommandOutput.unexpectedOutput |
| 753 && testCase.configuration['verbose'] != null | 932 && testCase.configuration['verbose'] != null |
| 754 && testCase.configuration['verbose']) { | 933 && testCase.configuration['verbose']) { |
| 755 print(testCase.displayName); | 934 print(testCase.displayName); |
| 756 for (var line in lastCommandOutput.stderr) print(line); | 935 |
| 757 for (var line in lastCommandOutput.stdout) print(line); | 936 print(new String.fromCharCodes(lastCommandOutput.stderr)); |
| 937 if (!lastCommandOutput.command.isPixelTest) { |
| 938 print(new String.fromCharCodes(lastCommandOutput.stdout)); |
| 939 } else { |
| 940 print("DRT pixel test failed! stdout is not printed because it " |
| 941 "contains binary data!"); |
| 942 } |
| 943 print(''); |
| 944 if (notifications.length > 0) { |
| 945 print("Notifications:"); |
| 946 for (var line in notifications) { |
| 947 print(notifications); |
| 948 } |
| 949 print(''); |
| 950 } |
| 758 } | 951 } |
| 759 if (allowRetries && testCase.usesWebDriver | 952 if (allowRetries && testCase.usesWebDriver |
| 760 && lastCommandOutput.unexpectedOutput | 953 && lastCommandOutput.unexpectedOutput |
| 761 && (testCase as BrowserTestCase).numRetries > 0) { | 954 && (testCase as BrowserTestCase).numRetries > 0) { |
| 762 // Selenium tests can be flaky. Try rerunning. | 955 // Selenium tests can be flaky. Try rerunning. |
| 763 lastCommandOutput.requestRetry = true; | 956 lastCommandOutput.requestRetry = true; |
| 764 } | 957 } |
| 765 if (lastCommandOutput.requestRetry) { | 958 if (lastCommandOutput.requestRetry) { |
| 766 lastCommandOutput.requestRetry = false; | 959 lastCommandOutput.requestRetry = false; |
| 767 this.timedOut = false; | 960 this.timedOut = false; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 789 if (timedOut) { | 982 if (timedOut) { |
| 790 // Non-webdriver test timed out before it could complete. Webdriver tests | 983 // 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 | 984 // run their own timeouts by timing from the launch of the browser (which |
| 792 // could be delayed). | 985 // could be delayed). |
| 793 testComplete(createCommandOutput(command, 0, true)); | 986 testComplete(createCommandOutput(command, 0, true)); |
| 794 } else if (currentStep == totalSteps) { | 987 } else if (currentStep == totalSteps) { |
| 795 // Done with all test commands. | 988 // Done with all test commands. |
| 796 testComplete(createCommandOutput(command, exitCode, false)); | 989 testComplete(createCommandOutput(command, exitCode, false)); |
| 797 } else if (exitCode != 0) { | 990 } else if (exitCode != 0) { |
| 798 // One of the steps failed. | 991 // One of the steps failed. |
| 799 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n'); | 992 notifications.add('test.dart: Compilation failed$suffix, ' |
| 993 'exit code $exitCode\n'); |
| 800 testComplete(createCommandOutput(command, exitCode, true)); | 994 testComplete(createCommandOutput(command, exitCode, true)); |
| 801 } else { | 995 } else { |
| 802 createCommandOutput(command, exitCode, true); | 996 createCommandOutput(command, exitCode, true); |
| 803 // One compilation step successfully completed, move on to the | 997 // One compilation step successfully completed, move on to the |
| 804 // next step. | 998 // next step. |
| 805 stderr.add('test.dart: Compilation finished $suffix\n'); | 999 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 && | 1000 if (currentStep == totalSteps - 1 && testCase.usesWebDriver && |
| 808 !testCase.configuration['noBatch']) { | 1001 !testCase.configuration['noBatch']) { |
| 809 // Note: processQueue will always be non-null for runtime == ie9, ie10, | 1002 // Note: processQueue will always be non-null for runtime == ie9, ie10, |
| 810 // ff, safari, chrome, opera. (It is only null for runtime == vm) | 1003 // ff, safari, chrome, opera. (It is only null for runtime == vm) |
| 811 // This RunningProcess object is done, and hands over control to | 1004 // This RunningProcess object is done, and hands over control to |
| 812 // BatchRunner.startTest(), which handles reporting, etc. | 1005 // BatchRunner.startTest(), which handles reporting, etc. |
| 813 if (timeoutTimer != null) { | 1006 if (timeoutTimer != null) { |
| 814 timeoutTimer.cancel(); | 1007 timeoutTimer.cancel(); |
| 815 } | 1008 } |
| 816 processQueue._getBatchRunner(testCase).startTest(testCase); | 1009 processQueue._getBatchRunner(testCase).startTest(testCase); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 834 timedOut, | 1027 timedOut, |
| 835 stdout, | 1028 stdout, |
| 836 stderr, | 1029 stderr, |
| 837 new Date.now().difference(startTime), | 1030 new Date.now().difference(startTime), |
| 838 compilationSkipped); | 1031 compilationSkipped); |
| 839 resetLocalOutputInformation(); | 1032 resetLocalOutputInformation(); |
| 840 return commandOutput; | 1033 return commandOutput; |
| 841 } | 1034 } |
| 842 | 1035 |
| 843 void resetLocalOutputInformation() { | 1036 void resetLocalOutputInformation() { |
| 844 stdout = new List<String>(); | 1037 stdout = new List<int>(); |
| 845 stderr = new List<String>(); | 1038 stderr = new List<int>(); |
| 1039 notifications = new List<String>(); |
| 846 compilationSkipped = false; | 1040 compilationSkipped = false; |
| 847 } | 1041 } |
| 848 | 1042 |
| 849 VoidFunction makeReadHandler(StringInputStream source, | 1043 void drainStream(InputStream source, List<int> destination) { |
| 850 List<String> destination) { | 1044 void onDataHandler () { |
| 851 void handler () { | 1045 if (source.closed) { |
| 852 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. | 1046 return; // TODO(whesse): Remove when bug is fixed. |
| 853 var line = source.readLine(); | 1047 } |
| 854 while (null != line) { | 1048 var data = source.read(); |
| 855 destination.add(line); | 1049 while (data != null) { |
| 856 line = source.readLine(); | 1050 destination.addAll(data); |
| 1051 data = source.read(); |
| 857 } | 1052 } |
| 858 } | 1053 } |
| 859 return handler; | 1054 source.onData = onDataHandler; |
| 1055 source.onClosed = onDataHandler; |
| 860 } | 1056 } |
| 861 | 1057 |
| 862 void start() { | 1058 void start() { |
| 863 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); | 1059 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); |
| 864 resetLocalOutputInformation(); | 1060 resetLocalOutputInformation(); |
| 865 currentStep = 0; | 1061 currentStep = 0; |
| 866 startTime = new Date.now(); | 1062 startTime = new Date.now(); |
| 867 runCommand(testCase.commands[currentStep++], commandComplete); | 1063 runCommand(testCase.commands[currentStep++], commandComplete); |
| 868 } | 1064 } |
| 869 | 1065 |
| 870 void runCommand(Command command, void commandCompleteHandler(Command, int)) { | 1066 void runCommand(Command command, void commandCompleteHandler(Command, int)) { |
| 871 void processExitHandler(int returnCode) { | 1067 void processExitHandler(int returnCode) { |
| 872 commandCompleteHandler(command, returnCode); | 1068 commandCompleteHandler(command, returnCode); |
| 873 } | 1069 } |
| 874 | 1070 |
| 875 command.outputIsUpToDate.then((bool isUpToDate) { | 1071 command.outputIsUpToDate.then((bool isUpToDate) { |
| 876 if (isUpToDate) { | 1072 if (isUpToDate) { |
| 877 stdout.add("Skipped compilation because the old output is " | 1073 notifications.add("Skipped compilation because the old output is " |
| 878 "still up to date!"); | 1074 "still up to date!"); |
| 879 compilationSkipped = true; | 1075 compilationSkipped = true; |
| 880 commandComplete(command, 0); | 1076 commandComplete(command, 0); |
| 881 } else { | 1077 } else { |
| 882 ProcessOptions options = new ProcessOptions(); | 1078 ProcessOptions options = new ProcessOptions(); |
| 883 options.environment = | 1079 if (command.environment != null) { |
| 884 new Map<String, String>.from(Platform.environment); | 1080 options.environment = |
| 1081 new Map<String, String>.from(command.environment); |
| 1082 } else { |
| 1083 options.environment = |
| 1084 new Map<String, String>.from(Platform.environment); |
| 1085 } |
| 1086 |
| 885 options.environment['DART_CONFIGURATION'] = | 1087 options.environment['DART_CONFIGURATION'] = |
| 886 TestUtils.configurationDir(testCase.configuration); | 1088 TestUtils.configurationDir(testCase.configuration); |
| 887 Future processFuture = Process.start(command.executable, | 1089 Future processFuture = Process.start(command.executable, |
| 888 command.arguments, | 1090 command.arguments, |
| 889 options); | 1091 options); |
| 890 processFuture.then((Process p) { | 1092 processFuture.then((Process p) { |
| 891 process = p; | 1093 process = p; |
| 892 process.onExit = processExitHandler; | 1094 process.onExit = processExitHandler; |
| 893 var stdoutStringStream = new StringInputStream(process.stdout); | 1095 drainStream(process.stdout, stdout); |
| 894 var stderrStringStream = new StringInputStream(process.stderr); | 1096 drainStream(process.stderr, stderr); |
| 895 stdoutStringStream.onLine = | |
| 896 makeReadHandler(stdoutStringStream, stdout); | |
| 897 stderrStringStream.onLine = | |
| 898 makeReadHandler(stderrStringStream, stderr); | |
| 899 if (timeoutTimer == null) { | 1097 if (timeoutTimer == null) { |
| 900 // Create one timeout timer when starting test case, remove it at | 1098 // Create one timeout timer when starting test case, remove it at |
| 901 // the end. | 1099 // the end. |
| 902 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); | 1100 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); |
| 903 } | 1101 } |
| 904 // If the timeout fired in between two commands, kill the just | 1102 // If the timeout fired in between two commands, kill the just |
| 905 // started process immediately. | 1103 // started process immediately. |
| 906 if (timedOut) safeKill(process); | 1104 if (timedOut) safeKill(process); |
| 907 }); | 1105 }); |
| 908 processFuture.handleException((e) { | 1106 processFuture.handleException((e) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 944 class BatchRunnerProcess { | 1142 class BatchRunnerProcess { |
| 945 Command _command; | 1143 Command _command; |
| 946 String _executable; | 1144 String _executable; |
| 947 List<String> _batchArguments; | 1145 List<String> _batchArguments; |
| 948 | 1146 |
| 949 Process _process; | 1147 Process _process; |
| 950 StringInputStream _stdoutStream; | 1148 StringInputStream _stdoutStream; |
| 951 StringInputStream _stderrStream; | 1149 StringInputStream _stderrStream; |
| 952 | 1150 |
| 953 TestCase _currentTest; | 1151 TestCase _currentTest; |
| 954 List<String> _testStdout; | 1152 List<int> _testStdout; |
| 955 List<String> _testStderr; | 1153 List<int> _testStderr; |
| 956 String _status; | 1154 String _status; |
| 957 bool _stdoutDrained = false; | 1155 bool _stdoutDrained = false; |
| 958 bool _stderrDrained = false; | 1156 bool _stderrDrained = false; |
| 959 MutableValue<bool> _ignoreStreams; | 1157 MutableValue<bool> _ignoreStreams; |
| 960 Date _startTime; | 1158 Date _startTime; |
| 961 Timer _timer; | 1159 Timer _timer; |
| 962 | 1160 |
| 963 bool _isWebDriver; | 1161 bool _isWebDriver; |
| 964 | 1162 |
| 965 BatchRunnerProcess(TestCase testCase) { | 1163 BatchRunnerProcess(TestCase testCase) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1022 } | 1220 } |
| 1023 | 1221 |
| 1024 void doStartTest(TestCase testCase) { | 1222 void doStartTest(TestCase testCase) { |
| 1025 _startTime = new Date.now(); | 1223 _startTime = new Date.now(); |
| 1026 _testStdout = []; | 1224 _testStdout = []; |
| 1027 _testStderr = []; | 1225 _testStderr = []; |
| 1028 _status = null; | 1226 _status = null; |
| 1029 _stdoutDrained = false; | 1227 _stdoutDrained = false; |
| 1030 _stderrDrained = false; | 1228 _stderrDrained = false; |
| 1031 _ignoreStreams = new MutableValue<bool>(false); // Captured by closures. | 1229 _ignoreStreams = new MutableValue<bool>(false); // Captured by closures. |
| 1032 _stdoutStream.onLine = _readStdout(_stdoutStream, _testStdout); | 1230 _readStdout(_stdoutStream, _testStdout); |
| 1033 _stderrStream.onLine = _readStderr(_stderrStream, _testStderr); | 1231 _readStderr(_stderrStream, _testStderr); |
| 1034 _timer = new Timer(testCase.timeout * 1000, _timeoutHandler); | 1232 _timer = new Timer(testCase.timeout * 1000, _timeoutHandler); |
| 1233 |
| 1234 if (testCase.commands.last.environment != null) { |
| 1235 print("Warning: command.environment != null, but we don't support custom " |
| 1236 "environments for batch runner tests!"); |
| 1237 } |
| 1238 |
| 1035 var line = _createArgumentsLine(testCase.batchTestArguments); | 1239 var line = _createArgumentsLine(testCase.batchTestArguments); |
| 1036 _process.stdin.onError = (err) { | 1240 _process.stdin.onError = (err) { |
| 1037 print('Error on batch runner input stream stdin'); | 1241 print('Error on batch runner input stream stdin'); |
| 1038 print(' Input line: $line'); | 1242 print(' Input line: $line'); |
| 1039 print(' Previous test\'s status: $_status'); | 1243 print(' Previous test\'s status: $_status'); |
| 1040 print(' Error: $err'); | 1244 print(' Error: $err'); |
| 1041 throw err; | 1245 throw err; |
| 1042 }; | 1246 }; |
| 1043 _process.stdin.write(line.charCodes); | 1247 _process.stdin.write(line.charCodes); |
| 1044 } | 1248 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1074 // Move on when both stdout and stderr has been drained. | 1278 // Move on when both stdout and stderr has been drained. |
| 1075 if (_stdoutDrained) _reportResult(); | 1279 if (_stdoutDrained) _reportResult(); |
| 1076 } | 1280 } |
| 1077 | 1281 |
| 1078 void _stdoutDone() { | 1282 void _stdoutDone() { |
| 1079 _stdoutDrained = true; | 1283 _stdoutDrained = true; |
| 1080 // Move on when both stdout and stderr has been drained. | 1284 // Move on when both stdout and stderr has been drained. |
| 1081 if (_stderrDrained) _reportResult(); | 1285 if (_stderrDrained) _reportResult(); |
| 1082 } | 1286 } |
| 1083 | 1287 |
| 1084 VoidFunction _readStdout(StringInputStream stream, List<String> buffer) { | 1288 void _readStdout(StringInputStream stream, List<int> buffer) { |
| 1085 var ignoreStreams = _ignoreStreams; // Capture this mutable object. | 1289 var ignoreStreams = _ignoreStreams; // Capture this mutable object. |
| 1086 void reader() { | 1290 void onLineHandler() { |
| 1087 if (ignoreStreams.value) { | 1291 if (ignoreStreams.value) { |
| 1088 while (stream.readLine() != null) { | 1292 while (stream.readLine() != null) { |
| 1089 // Do nothing. | 1293 // Do nothing. |
| 1090 } | 1294 } |
| 1091 return; | 1295 return; |
| 1092 } | 1296 } |
| 1093 // Otherwise, process output and call _reportResult() when done. | 1297 // Otherwise, process output and call _reportResult() when done. |
| 1094 var line = stream.readLine(); | 1298 var line = stream.readLine(); |
| 1095 while (line != null) { | 1299 while (line != null) { |
| 1096 if (line.startsWith('>>> TEST')) { | 1300 if (line.startsWith('>>> TEST')) { |
| 1097 _status = line; | 1301 _status = line; |
| 1098 } else if (line.startsWith('>>> BATCH START')) { | 1302 } else if (line.startsWith('>>> BATCH START')) { |
| 1099 // ignore | 1303 // ignore |
| 1100 } else if (line.startsWith('>>> ')) { | 1304 } else if (line.startsWith('>>> ')) { |
| 1101 throw new Exception('Unexpected command from dartc batch runner.'); | 1305 throw new Exception('Unexpected command from dartc batch runner.'); |
| 1102 } else { | 1306 } else { |
| 1103 buffer.add(line); | 1307 buffer.addAll("$line\n".charCodes); |
| 1104 } | 1308 } |
| 1105 line = stream.readLine(); | 1309 line = stream.readLine(); |
| 1106 } | 1310 } |
| 1107 if (_status != null) { | 1311 if (_status != null) { |
| 1108 _timer.cancel(); | 1312 _timer.cancel(); |
| 1109 _stdoutDone(); | 1313 _stdoutDone(); |
| 1110 } | 1314 } |
| 1111 } | 1315 } |
| 1112 return reader; | 1316 stream.onLine = onLineHandler; |
| 1113 } | 1317 } |
| 1114 | 1318 |
| 1115 VoidFunction _readStderr(StringInputStream stream, List<String> buffer) { | 1319 void _readStderr(StringInputStream stream, List<int> buffer) { |
| 1116 var ignoreStreams = _ignoreStreams; // Capture this mutable object. | 1320 var ignoreStreams = _ignoreStreams; // Capture this mutable object. |
| 1117 void reader() { | 1321 void onLineHandler() { |
| 1118 if (ignoreStreams.value) { | 1322 if (ignoreStreams.value) { |
| 1119 while (stream.readLine() != null) { | 1323 while (stream.readLine() != null) { |
| 1120 // Do nothing. | 1324 // Do nothing. |
| 1121 } | 1325 } |
| 1122 return; | 1326 return; |
| 1123 } | 1327 } |
| 1124 // Otherwise, process output and call _reportResult() when done. | 1328 // Otherwise, process output and call _reportResult() when done. |
| 1125 var line = stream.readLine(); | 1329 var line = stream.readLine(); |
| 1126 while (line != null) { | 1330 while (line != null) { |
| 1127 if (line.startsWith('>>> EOF STDERR')) { | 1331 if (line.startsWith('>>> EOF STDERR')) { |
| 1128 _stderrDone(); | 1332 _stderrDone(); |
| 1129 } else { | 1333 } else { |
| 1130 buffer.add(line); | 1334 buffer.addAll("$line\n".charCodes); |
| 1131 } | 1335 } |
| 1132 line = stream.readLine(); | 1336 line = stream.readLine(); |
| 1133 } | 1337 } |
| 1134 } | 1338 } |
| 1135 return reader; | 1339 stream.onLine = onLineHandler; |
| 1136 } | 1340 } |
| 1137 | 1341 |
| 1138 ExitCodeEvent makeExitHandler(String status) { | 1342 ExitCodeEvent makeExitHandler(String status) { |
| 1139 void handler(int exitCode) { | 1343 void handler(int exitCode) { |
| 1140 if (active) { | 1344 if (active) { |
| 1141 if (_timer != null) _timer.cancel(); | 1345 if (_timer != null) _timer.cancel(); |
| 1142 _status = status; | 1346 _status = status; |
| 1143 // Read current content of streams, ignore any later output. | 1347 // Read current content of streams, ignore any later output. |
| 1144 _ignoreStreams.value = true; | 1348 _ignoreStreams.value = true; |
| 1145 var line = _stdoutStream.readLine(); | 1349 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 | 1725 // 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 | 1726 // tests that appear to be broken but were actually just flakes that |
| 1523 // didn't get retried because there had already been one failure. | 1727 // didn't get retried because there had already been one failure. |
| 1524 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; | 1728 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 1525 new RunningProcess(test, allowRetry, this).start(); | 1729 new RunningProcess(test, allowRetry, this).start(); |
| 1526 } | 1730 } |
| 1527 _numProcesses++; | 1731 _numProcesses++; |
| 1528 } | 1732 } |
| 1529 } | 1733 } |
| 1530 } | 1734 } |
| OLD | NEW |