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

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

Issue 11091070: Change Process.start to return a future that completes with a (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Restructure to get rid of _onStart and _onError Created 8 years, 2 months 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
« no previous file with comments | « tools/testing/dart/co19_test.dart ('k') | tools/testing/dart/test_suite.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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.
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("status_file_parser.dart"); 16 #import("status_file_parser.dart");
17 #import("test_progress.dart"); 17 #import("test_progress.dart");
18 #import("test_suite.dart"); 18 #import("test_suite.dart");
19 19
20 const int NO_TIMEOUT = 0; 20 const int NO_TIMEOUT = 0;
21 const int SLOW_TIMEOUT_MULTIPLIER = 4; 21 const int SLOW_TIMEOUT_MULTIPLIER = 4;
22 22
23 typedef void TestCaseEvent(TestCase testCase); 23 typedef void TestCaseEvent(TestCase testCase);
24 typedef void ExitCodeEvent(int exitCode); 24 typedef void ExitCodeEvent(int exitCode);
25 typedef bool EnqueueMoreWork(ProcessQueue queue); 25 typedef void EnqueueMoreWork(ProcessQueue queue);
26 26
27 /** A command executed as a step in a test case. */ 27 /** A command executed as a step in a test case. */
28 class Command { 28 class Command {
29 /** Path to the executable of this command. */ 29 /** Path to the executable of this command. */
30 String executable; 30 String executable;
31 31
32 /** Command line arguments to the executable. */ 32 /** Command line arguments to the executable. */
33 List<String> arguments; 33 List<String> arguments;
34 34
35 /** The actual command line that will be executed. */ 35 /** The actual command line that will be executed. */
(...skipping 566 matching lines...) Expand 10 before | Expand all | Expand 10 after
602 } 602 }
603 } 603 }
604 604
605 /** 605 /**
606 * Process exit handler called at the end of every command. It internally 606 * Process exit handler called at the end of every command. It internally
607 * treats all but the last command as compilation steps. The last command is 607 * treats all but the last command as compilation steps. The last command is
608 * the actual test and its output is analyzed in [testComplete]. 608 * the actual test and its output is analyzed in [testComplete].
609 */ 609 */
610 void stepExitHandler(int exitCode) { 610 void stepExitHandler(int exitCode) {
611 process.close(); 611 process.close();
612 process = null;
612 int totalSteps = testCase.commands.length; 613 int totalSteps = testCase.commands.length;
613 String suffix =' (step $currentStep of $totalSteps)'; 614 String suffix =' (step $currentStep of $totalSteps)';
614 if (currentStep == totalSteps) { // done with test command 615 if (timedOut) {
616 // Test timed out before it could complete.
617 testComplete(0, true);
618 } else if (currentStep == totalSteps) {
619 // Done with all test commands.
615 testComplete(exitCode, false); 620 testComplete(exitCode, false);
616 } else if (exitCode != 0) { 621 } else if (exitCode != 0) {
622 // One of the steps failed.
617 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n'); 623 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n');
618 testComplete(exitCode, true); 624 testComplete(exitCode, true);
619 } else { 625 } else {
626 // One compilation step successfully completed, move on to the
627 // next step.
620 stderr.add('test.dart: Compilation finished $suffix\n'); 628 stderr.add('test.dart: Compilation finished $suffix\n');
621 stdout.add('test.dart: Compilation finished $suffix\n'); 629 stdout.add('test.dart: Compilation finished $suffix\n');
622 if (currentStep == totalSteps - 1 && testCase.usesWebDriver && 630 if (currentStep == totalSteps - 1 && testCase.usesWebDriver &&
623 !testCase.configuration['noBatch']) { 631 !testCase.configuration['noBatch']) {
624 // Note: processQueue will always be non-null for runtime == ie, ff, 632 // Note: processQueue will always be non-null for runtime == ie, ff,
625 // safari, chrome, opera. (It is only null for runtime == vm) 633 // safari, chrome, opera. (It is only null for runtime == vm)
626 // This RunningProcess object is done, and hands over control to 634 // This RunningProcess object is done, and hands over control to
627 // BatchRunner.startTest(), which handles reporting, etc. 635 // BatchRunner.startTest(), which handles reporting, etc.
628 timeoutTimer.cancel(); 636 timeoutTimer.cancel();
629 processQueue._getBatchRunner(testCase).startTest(testCase); 637 processQueue._getBatchRunner(testCase).startTest(testCase);
(...skipping 19 matching lines...) Expand all
649 void start() { 657 void start() {
650 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); 658 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP));
651 stdout = new List<String>(); 659 stdout = new List<String>();
652 stderr = new List<String>(); 660 stderr = new List<String>();
653 currentStep = 0; 661 currentStep = 0;
654 startTime = new Date.now(); 662 startTime = new Date.now();
655 runCommand(testCase.commands[currentStep++], stepExitHandler); 663 runCommand(testCase.commands[currentStep++], stepExitHandler);
656 } 664 }
657 665
658 void runCommand(Command command, void exitHandler(int exitCode)) { 666 void runCommand(Command command, void exitHandler(int exitCode)) {
659 process = Process.start(command.executable, command.arguments); 667 Future processFuture = Process.start(command.executable, command.arguments);
660 process.onExit = exitHandler; 668 processFuture.then((Process p) {
661 process.onError = (e) { 669 process = p;
662 print("Error starting process:"); 670 process.onExit = exitHandler;
671 var stdoutStringStream = new StringInputStream(process.stdout);
672 var stderrStringStream = new StringInputStream(process.stderr);
673 stdoutStringStream.onLine =
674 makeReadHandler(stdoutStringStream, stdout);
675 stderrStringStream.onLine =
676 makeReadHandler(stderrStringStream, stderr);
677 if (timeoutTimer == null) {
678 // Create one timeout timer when starting test case, remove it at end.
679 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler);
680 }
681 // If the timeout fired in between two commands, kill the just
682 // started process immediately.
683 if (timedOut) process.kill();
684 });
685 processFuture.handleException((e) {
686 print("Process error:");
663 print(" Command: $command"); 687 print(" Command: $command");
664 print(" Error: $e"); 688 print(" Error: $e");
665 testComplete(-1, false); 689 testComplete(-1, false);
666 }; 690 return true;
667 InputStream stdoutStream = process.stdout; 691 });
668 InputStream stderrStream = process.stderr;
669 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream);
670 StringInputStream stderrStringStream = new StringInputStream(stderrStream);
671 stdoutStringStream.onLine =
672 makeReadHandler(stdoutStringStream, stdout);
673 stderrStringStream.onLine =
674 makeReadHandler(stderrStringStream, stderr);
675 if (timeoutTimer == null) {
676 // Create one timeout timer when starting test case, remove it at end.
677 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler);
678 }
679 } 692 }
680 693
681 void timeoutHandler(Timer unusedTimer) { 694 void timeoutHandler(Timer unusedTimer) {
682 timedOut = true; 695 timedOut = true;
683 process.kill(); 696 if (process != null) process.kill();
684 } 697 }
685 } 698 }
686 699
687 /** 700 /**
688 * This class holds a value, that can be changed. It is used when 701 * This class holds a value, that can be changed. It is used when
689 * closures need a shared value, that they can all change and read. 702 * closures need a shared value, that they can all change and read.
690 */ 703 */
691 class MutableValue<T> { 704 class MutableValue<T> {
692 MutableValue(T this.value); 705 MutableValue(T this.value);
693 T value; 706 T value;
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
894 line = _stdoutStream.readLine(); 907 line = _stdoutStream.readLine();
895 } 908 }
896 line = _stderrStream.readLine(); 909 line = _stderrStream.readLine();
897 while (line != null) { 910 while (line != null) {
898 _testStderr.add(line); 911 _testStderr.add(line);
899 line = _stderrStream.readLine(); 912 line = _stderrStream.readLine();
900 } 913 }
901 _stderrDrained = true; 914 _stderrDrained = true;
902 _stdoutDrained = true; 915 _stdoutDrained = true;
903 _process.close(); 916 _process.close();
904 _startProcess(() { _reportResult(); }); 917 _startProcess(_reportResult);
905 } else { // No active test case running. 918 } else { // No active test case running.
906 _process.close(); 919 _process.close();
907 _process = null; 920 _process = null;
908 } 921 }
909 } 922 }
910 return handler; 923 return handler;
911 } 924 }
912 925
913 void _timeoutHandler(ignore) { 926 void _timeoutHandler(ignore) {
914 _process.onExit = makeExitHandler(">>> TEST TIMEOUT"); 927 _process.onExit = makeExitHandler(">>> TEST TIMEOUT");
915 _process.kill(); 928 _process.kill();
916 } 929 }
917 930
918 void _startProcess(then) { 931 _startProcess(callback) {
919 _process = Process.start(_executable, _batchArguments); 932 Future processFuture = Process.start(_executable, _batchArguments);
920 _stdoutStream = new StringInputStream(_process.stdout); 933 processFuture.then((Process p) {
921 _stderrStream = new StringInputStream(_process.stderr); 934 _process = p;
922 _process.onExit = makeExitHandler(">>> TEST CRASH"); 935 _stdoutStream = new StringInputStream(_process.stdout);
923 _process.onError = (e) { 936 _stderrStream = new StringInputStream(_process.stderr);
924 print("Error starting process:"); 937 _process.onExit = makeExitHandler(">>> TEST CRASH");
938 callback();
939 });
940 processFuture.handleException((e) {
941 print("Process error:");
925 print(" Command: $_executable ${Strings.join(_batchArguments, ' ')}"); 942 print(" Command: $_executable ${Strings.join(_batchArguments, ' ')}");
926 print(" Error: $e"); 943 print(" Error: $e");
927 // If there is an error starting a batch process, chances are that 944 // If there is an error starting a batch process, chances are that
928 // it will always fail. So rather than re-trying a 1000+ times, we 945 // it will always fail. So rather than re-trying a 1000+ times, we
929 // exit. 946 // exit.
930 exit(1); 947 exit(1);
931 }; 948 return true;
932 _process.onStart = then; 949 });
933 } 950 }
934 } 951 }
935 952
936 /** 953 /**
937 * ProcessQueue is the master control class, responsible for running all 954 * ProcessQueue is the master control class, responsible for running all
938 * the tests in all the TestSuites that have been registered. It includes 955 * the tests in all the TestSuites that have been registered. It includes
939 * a rate-limited queue to run a limited number of tests in parallel, 956 * a rate-limited queue to run a limited number of tests in parallel,
940 * a ProgressIndicator which prints output when tests are started and 957 * a ProgressIndicator which prints output when tests are started and
941 * and completed, and a summary report when all tests are completed, 958 * and completed, and a summary report when all tests are completed,
942 * and counters to determine when all of the tests in all of the test suites 959 * and counters to determine when all of the tests in all of the test suites
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
1071 if (!_isSeleniumAvailable && !_startingServer) { 1088 if (!_isSeleniumAvailable && !_startingServer) {
1072 _startingServer = true; 1089 _startingServer = true;
1073 1090
1074 // Check to see if the jar was already running before the program started. 1091 // Check to see if the jar was already running before the program started.
1075 String cmd = 'ps'; 1092 String cmd = 'ps';
1076 var arg = ['aux']; 1093 var arg = ['aux'];
1077 if (Platform.operatingSystem == 'windows') { 1094 if (Platform.operatingSystem == 'windows') {
1078 cmd = 'tasklist'; 1095 cmd = 'tasklist';
1079 arg.add('/v'); 1096 arg.add('/v');
1080 } 1097 }
1081 Process p = Process.start(cmd, arg); 1098
1082 final StringInputStream stdoutStringStream = 1099 Future processFuture = Process.start(cmd, arg);
1083 new StringInputStream(p.stdout); 1100 processFuture.then((Process p) {
1084 p.onError = (e) { 1101 final StringInputStream stdoutStringStream =
1102 new StringInputStream(p.stdout);
1103 stdoutStringStream.onLine = () {
1104 var line = stdoutStringStream.readLine();
1105 while (null != line) {
1106 var regexp = const RegExp(r".*selenium-server-standalone.*");
1107 if (regexp.hasMatch(line)) {
1108 _seleniumAlreadyRunning = true;
1109 resumeTesting();
1110 }
1111 line = stdoutStringStream.readLine();
1112 }
1113 if (!_isSeleniumAvailable) {
1114 _startSeleniumServer();
1115 }
1116 };
1117 });
1118 processFuture.handleException((e) {
1085 print("Error starting process:"); 1119 print("Error starting process:");
1086 print(" Command: $cmd ${Strings.join(arg, ' ')}"); 1120 print(" Command: $cmd ${Strings.join(arg, ' ')}");
1087 print(" Error: $e"); 1121 print(" Error: $e");
1088 // TODO(ahe): How to report this as a test failure? 1122 // TODO(ahe): How to report this as a test failure?
1089 exit(1); 1123 exit(1);
1090 }; 1124 return true;
1091 stdoutStringStream.onLine = () { 1125 });
1092 var line = stdoutStringStream.readLine();
1093 while (null != line) {
1094 if (const RegExp(r".*selenium-server-standalone.*").hasMatch(line)) {
1095 _seleniumAlreadyRunning = true;
1096 resumeTesting();
1097 }
1098 line = stdoutStringStream.readLine();
1099 }
1100 if (!_isSeleniumAvailable) {
1101 _startSeleniumServer();
1102 }
1103 };
1104 } 1126 }
1105 } 1127 }
1106 1128
1107 void _runTest(TestCase test) { 1129 void _runTest(TestCase test) {
1108 if (test.usesWebDriver) { 1130 if (test.usesWebDriver) {
1109 browserUsed = test.configuration['browser']; 1131 browserUsed = test.configuration['browser'];
1110 if (_needsSelenium) _ensureSeleniumServerRunning(); 1132 if (_needsSelenium) _ensureSeleniumServerRunning();
1111 } 1133 }
1112 _progress.testAdded(); 1134 _progress.testAdded();
1113 _tests.add(test); 1135 _tests.add(test);
(...skipping 28 matching lines...) Expand all
1142 void _startSeleniumServer() { 1164 void _startSeleniumServer() {
1143 // Get the absolute path to the Selenium jar. 1165 // Get the absolute path to the Selenium jar.
1144 String filePath = TestUtils.testScriptPath; 1166 String filePath = TestUtils.testScriptPath;
1145 String pathSep = Platform.pathSeparator; 1167 String pathSep = Platform.pathSeparator;
1146 int index = filePath.lastIndexOf(pathSep); 1168 int index = filePath.lastIndexOf(pathSep);
1147 filePath = '${filePath.substring(0, index)}${pathSep}testing${pathSep}'; 1169 filePath = '${filePath.substring(0, index)}${pathSep}testing${pathSep}';
1148 var lister = new Directory(filePath).list(); 1170 var lister = new Directory(filePath).list();
1149 lister.onFile = (String file) { 1171 lister.onFile = (String file) {
1150 if (const RegExp(r"selenium-server-standalone-.*\.jar").hasMatch(file) 1172 if (const RegExp(r"selenium-server-standalone-.*\.jar").hasMatch(file)
1151 && _seleniumServer == null) { 1173 && _seleniumServer == null) {
1152 _seleniumServer = Process.start('java', ['-jar', file]); 1174 Future processFuture = Process.start('java', ['-jar', file]);
1153 _seleniumServer.onError = (e) { 1175 processFuture.then((Process server) {
1154 print("Error starting process:"); 1176 _seleniumServer = server;
1177 // Heads up: there seems to an obscure data race of some form in
1178 // the VM between launching the server process and launching the test
1179 // tasks that disappears when you read IO (which is convenient, since
1180 // that is our condition for knowing that the server is ready).
1181 StringInputStream stdoutStringStream =
1182 new StringInputStream(_seleniumServer.stdout);
1183 StringInputStream stderrStringStream =
1184 new StringInputStream(_seleniumServer.stderr);
1185 stdoutStringStream.onLine =
1186 makeSeleniumServerHandler(stdoutStringStream);
1187 stderrStringStream.onLine =
1188 makeSeleniumServerHandler(stderrStringStream);
1189 });
1190 processFuture.handleException((e) {
1191 print("Process error:");
1155 print(" Command: java -jar $file"); 1192 print(" Command: java -jar $file");
1156 print(" Error: $e"); 1193 print(" Error: $e");
1157 // TODO(ahe): How to report this as a test failure? 1194 // TODO(ahe): How to report this as a test failure?
1158 exit(1); 1195 exit(1);
1159 }; 1196 return true;
1160 // Heads up: there seems to an obscure data race of some form in 1197 });
1161 // the VM between launching the server process and launching the test
1162 // tasks that disappears when you read IO (which is convenient, since
1163 // that is our condition for knowing that the server is ready).
1164 StringInputStream stdoutStringStream =
1165 new StringInputStream(_seleniumServer.stdout);
1166 StringInputStream stderrStringStream =
1167 new StringInputStream(_seleniumServer.stderr);
1168 stdoutStringStream.onLine =
1169 makeSeleniumServerHandler(stdoutStringStream);
1170 stderrStringStream.onLine =
1171 makeSeleniumServerHandler(stderrStringStream);
1172 } 1198 }
1173 }; 1199 };
1174 } 1200 }
1175 1201
1176 Future _terminateBatchRunners() { 1202 Future _terminateBatchRunners() {
1177 var futures = new List(); 1203 var futures = new List();
1178 for (var runners in _batchProcesses.getValues()) { 1204 for (var runners in _batchProcesses.getValues()) {
1179 for (var runner in runners) { 1205 for (var runner in runners) {
1180 futures.add(runner.terminate()); 1206 futures.add(runner.terminate());
1181 } 1207 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 // the developer doesn't waste his or her time trying to fix a bunch of 1273 // the developer doesn't waste his or her time trying to fix a bunch of
1248 // tests that appear to be broken but were actually just flakes that 1274 // tests that appear to be broken but were actually just flakes that
1249 // didn't get retried because there had already been one failure. 1275 // didn't get retried because there had already been one failure.
1250 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; 1276 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests;
1251 new RunningProcess(test, allowRetry, this).start(); 1277 new RunningProcess(test, allowRetry, this).start();
1252 } 1278 }
1253 _numProcesses++; 1279 _numProcesses++;
1254 } 1280 }
1255 } 1281 }
1256 } 1282 }
OLDNEW
« no previous file with comments | « tools/testing/dart/co19_test.dart ('k') | tools/testing/dart/test_suite.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698