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