Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(32)

Side by Side Diff: tools/testing/dart/test_runner.dart

Issue 11586012: Call DumpRenderTree directly from test.dart instead of using the drt-trampoline.py indirection (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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;
ricow1 2012/12/17 13:45:57 file a bug for us to make this nicer by using poly
kustermann 2012/12/17 14:00:47 Done. Issue 7453
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
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
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
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
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,
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++) {
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
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
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('');
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698