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

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

Issue 11817012: Migration of testing scripts in tools/ to libv2 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 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
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:async";
15 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow
16 // CommandOutput.exitCode in subclasses of CommandOutput.
17 import "dart:io" as io;
15 import "dart:isolate"; 18 import "dart:isolate";
16 import "dart:uri"; 19 import "dart:uri";
17 import "status_file_parser.dart"; 20 import "status_file_parser.dart";
18 import "test_progress.dart"; 21 import "test_progress.dart";
19 import "test_suite.dart"; 22 import "test_suite.dart";
20 23
21 const int NO_TIMEOUT = 0; 24 const int NO_TIMEOUT = 0;
22 const int SLOW_TIMEOUT_MULTIPLIER = 4; 25 const int SLOW_TIMEOUT_MULTIPLIER = 4;
23 26
24 typedef void TestCaseEvent(TestCase testCase); 27 typedef void TestCaseEvent(TestCase testCase);
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
82 /** Command line arguments to the executable. */ 85 /** Command line arguments to the executable. */
83 List<String> arguments; 86 List<String> arguments;
84 87
85 /** Environment for the command */ 88 /** Environment for the command */
86 Map<String,String> environment; 89 Map<String,String> environment;
87 90
88 /** The actual command line that will be executed. */ 91 /** The actual command line that will be executed. */
89 String commandLine; 92 String commandLine;
90 93
91 Command(this.executable, this.arguments, [this.environment = null]) { 94 Command(this.executable, this.arguments, [this.environment = null]) {
92 if (Platform.operatingSystem == 'windows') { 95 if (io.Platform.operatingSystem == 'windows') {
93 // Windows can't handle the first command if it is a .bat file or the like 96 // Windows can't handle the first command if it is a .bat file or the like
94 // with the slashes going the other direction. 97 // with the slashes going the other direction.
95 // TODO(efortuna): Remove this when fixed (Issue 1306). 98 // TODO(efortuna): Remove this when fixed (Issue 1306).
96 executable = executable.replaceAll('/', '\\'); 99 executable = executable.replaceAll('/', '\\');
97 } 100 }
98 commandLine = "$executable ${Strings.join(arguments, ' ')}"; 101 commandLine = "$executable ${Strings.join(arguments, ' ')}";
99 } 102 }
100 103
101 String toString() => commandLine; 104 String toString() => commandLine;
102 105
103 Future<bool> get outputIsUpToDate => new Future.immediate(false); 106 Future<bool> get outputIsUpToDate => new Future.immediate(false);
104 Path get expectedOutputFile => null; 107 io.Path get expectedOutputFile => null;
105 bool get isPixelTest => false; 108 bool get isPixelTest => false;
106 } 109 }
107 110
108 class CompilationCommand extends Command { 111 class CompilationCommand extends Command {
109 String _outputFile; 112 String _outputFile;
110 bool _neverSkipCompilation; 113 bool _neverSkipCompilation;
111 List<Uri> _bootstrapDependencies; 114 List<Uri> _bootstrapDependencies;
112 115
113 CompilationCommand(this._outputFile, 116 CompilationCommand(this._outputFile,
114 this._neverSkipCompilation, 117 this._neverSkipCompilation,
115 this._bootstrapDependencies, 118 this._bootstrapDependencies,
116 String executable, 119 String executable,
117 List<String> arguments) 120 List<String> arguments)
118 : super(executable, arguments); 121 : super(executable, arguments);
119 122
120 Future<bool> get outputIsUpToDate { 123 Future<bool> get outputIsUpToDate {
121 if (_neverSkipCompilation) return new Future.immediate(false); 124 if (_neverSkipCompilation) return new Future.immediate(false);
122 125
123 Future<List<Uri>> readDepsFile(String path) { 126 Future<List<Uri>> readDepsFile(String path) {
124 var file = new File(new Path(path).toNativePath()); 127 var file = new io.File(new io.Path(path).toNativePath());
125 if (!file.existsSync()) { 128 if (!file.existsSync()) {
126 return new Future.immediate(null); 129 return new Future.immediate(null);
127 } 130 }
128 return file.readAsLines().transform((List<String> lines) { 131 return file.readAsLines().then((List<String> lines) {
129 var dependencies = new List<Uri>(); 132 var dependencies = new List<Uri>();
130 for (var line in lines) { 133 for (var line in lines) {
131 line = line.trim(); 134 line = line.trim();
132 if (line.length > 0) { 135 if (line.length > 0) {
133 dependencies.add(new Uri(line)); 136 dependencies.add(new Uri(line));
134 } 137 }
135 } 138 }
136 return dependencies; 139 return dependencies;
137 }); 140 });
138 } 141 }
139 142
140 return readDepsFile("$_outputFile.deps").transform((dependencies) { 143 return readDepsFile("$_outputFile.deps").then((dependencies) {
141 if (dependencies != null) { 144 if (dependencies != null) {
142 dependencies.addAll(_bootstrapDependencies); 145 dependencies.addAll(_bootstrapDependencies);
143 var jsOutputLastModified = TestUtils.lastModifiedCache.getLastModified( 146 var jsOutputLastModified = TestUtils.lastModifiedCache.getLastModified(
144 new Uri.fromComponents(scheme: 'file', path: _outputFile)); 147 new Uri.fromComponents(scheme: 'file', path: _outputFile));
145 if (jsOutputLastModified != null) { 148 if (jsOutputLastModified != null) {
146 for (var dependency in dependencies) { 149 for (var dependency in dependencies) {
147 var dependencyLastModified = 150 var dependencyLastModified =
148 TestUtils.lastModifiedCache.getLastModified(dependency); 151 TestUtils.lastModifiedCache.getLastModified(dependency);
149 if (dependencyLastModified == null || 152 if (dependencyLastModified == null ||
150 dependencyLastModified > jsOutputLastModified) { 153 dependencyLastModified > jsOutputLastModified) {
151 return false; 154 return false;
152 } 155 }
153 } 156 }
154 return true; 157 return true;
155 } 158 }
156 } 159 }
157 return false; 160 return false;
158 }); 161 });
159 } 162 }
160 } 163 }
161 164
162 class DumpRenderTreeCommand extends Command { 165 class DumpRenderTreeCommand extends Command {
163 /** 166 /**
164 * If [expectedOutputPath] is set, the output of DumpRenderTree is compared 167 * If [expectedOutputPath] is set, the output of DumpRenderTree is compared
165 * with the content of [expectedOutputPath]. 168 * with the content of [expectedOutputPath].
166 * This is used for example for pixel tests, where [expectedOutputPath] points 169 * This is used for example for pixel tests, where [expectedOutputPath] points
167 * to a *png file. 170 * to a *png file.
168 */ 171 */
169 Path expectedOutputPath; 172 io.Path expectedOutputPath;
170 173
171 DumpRenderTreeCommand(String executable, 174 DumpRenderTreeCommand(String executable,
172 String htmlFile, 175 String htmlFile,
173 List<String> options, 176 List<String> options,
174 List<String> dartFlags, 177 List<String> dartFlags,
175 Uri packageRootUri, 178 Uri packageRootUri,
176 Path this.expectedOutputPath) 179 io.Path this.expectedOutputPath)
177 : super(executable, 180 : super(executable,
178 _getArguments(options, htmlFile), 181 _getArguments(options, htmlFile),
179 _getEnvironment(dartFlags, packageRootUri)); 182 _getEnvironment(dartFlags, packageRootUri));
180 183
181 static Map _getEnvironment(List<String> dartFlags, Uri packageRootUri) { 184 static Map _getEnvironment(List<String> dartFlags, Uri packageRootUri) {
182 var needDartFlags = dartFlags != null && dartFlags.length > 0; 185 var needDartFlags = dartFlags != null && dartFlags.length > 0;
183 var needDartPackageRoot = packageRootUri != null; 186 var needDartPackageRoot = packageRootUri != null;
184 187
185 var env = null; 188 var env = null;
186 if (needDartFlags || needDartPackageRoot) { 189 if (needDartFlags || needDartPackageRoot) {
187 env = new Map.from(Platform.environment); 190 env = new Map.from(io.Platform.environment);
188 if (needDartFlags) { 191 if (needDartFlags) {
189 env['DART_FLAGS'] = Strings.join(dartFlags, " "); 192 env['DART_FLAGS'] = Strings.join(dartFlags, " ");
190 } 193 }
191 if (needDartPackageRoot) { 194 if (needDartPackageRoot) {
192 env['DART_PACKAGE_ROOT'] = packageRootUri.toString(); 195 env['DART_PACKAGE_ROOT'] = packageRootUri.toString();
193 } 196 }
194 } 197 }
195 198
196 return env; 199 return env;
197 } 200 }
198 201
199 static List<String> _getArguments(List<String> options, String htmlFile) { 202 static List<String> _getArguments(List<String> options, String htmlFile) {
200 var arguments = new List.from(options); 203 var arguments = new List.from(options);
201 arguments.add(htmlFile); 204 arguments.add(htmlFile);
202 return arguments; 205 return arguments;
203 } 206 }
204 207
205 Path get expectedOutputFile => expectedOutputPath; 208 io.Path get expectedOutputFile => expectedOutputPath;
206 bool get isPixelTest => (expectedOutputFile != null && 209 bool get isPixelTest => (expectedOutputFile != null &&
207 expectedOutputFile.filename.endsWith(".png")); 210 expectedOutputFile.filename.endsWith(".png"));
208 } 211 }
209 212
210 213
211 /** 214 /**
212 * TestCase contains all the information needed to run a test and evaluate 215 * TestCase contains all the information needed to run a test and evaluate
213 * its output. Running a test involves starting a separate process, with 216 * its output. Running a test involves starting a separate process, with
214 * the executable and arguments given by the TestCase, and recording its 217 * the executable and arguments given by the TestCase, and recording its
215 * stdout and stderr output streams, and its exit code. TestCase only 218 * stdout and stderr output streams, and its exit code. TestCase only
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
548 551
549 String get result => 552 String get result =>
550 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); 553 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS));
551 554
552 bool get unexpectedOutput => !testCase.expectedOutcomes.contains(result); 555 bool get unexpectedOutput => !testCase.expectedOutcomes.contains(result);
553 556
554 bool get hasCrashed { 557 bool get hasCrashed {
555 // The Java dartc runner and dart2js exits with code 253 in case 558 // The Java dartc runner and dart2js exits with code 253 in case
556 // of unhandled exceptions. 559 // of unhandled exceptions.
557 if (exitCode == 253) return true; 560 if (exitCode == 253) return true;
558 if (Platform.operatingSystem == 'windows') { 561 if (io.Platform.operatingSystem == 'windows') {
559 // The VM uses std::abort to terminate on asserts. 562 // The VM uses std::abort to terminate on asserts.
560 // std::abort terminates with exit code 3 on Windows. 563 // std::abort terminates with exit code 3 on Windows.
561 if (exitCode == 3) { 564 if (exitCode == 3) {
562 return !timedOut; 565 return !timedOut;
563 } 566 }
564 return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0)); 567 return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0));
565 } 568 }
566 return !timedOut && ((exitCode < 0)); 569 return !timedOut && ((exitCode < 0));
567 } 570 }
568 571
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
651 * Content-Length: ...\n 654 * Content-Length: ...\n
652 * <*png data> 655 * <*png data>
653 * #EOF\n 656 * #EOF\n
654 * So we need to get the byte-range of the png data first, before 657 * So we need to get the byte-range of the png data first, before
655 * comparing it with the content of the expected output file. 658 * comparing it with the content of the expected output file.
656 * 659 *
657 * On a layout tests, the DRT output is directly compared with the 660 * On a layout tests, the DRT output is directly compared with the
658 * content of the expected output. 661 * content of the expected output.
659 */ 662 */
660 var stdout = testCase.commandOutputs[command].stdout; 663 var stdout = testCase.commandOutputs[command].stdout;
661 var file = new File.fromPath(command.expectedOutputFile); 664 var file = new io.File.fromPath(command.expectedOutputFile);
662 if (file.existsSync()) { 665 if (file.existsSync()) {
663 var bytesContentLength = "Content-Length:".charCodes; 666 var bytesContentLength = "Content-Length:".charCodes;
664 var bytesNewLine = "\n".charCodes; 667 var bytesNewLine = "\n".charCodes;
665 var bytesEOF = "#EOF\n".charCodes; 668 var bytesEOF = "#EOF\n".charCodes;
666 669
667 var expectedContent = file.readAsBytesSync(); 670 var expectedContent = file.readAsBytesSync();
668 if (command.isPixelTest) { 671 if (command.isPixelTest) {
669 var startOfContentLength = findBytes(stdout, bytesContentLength); 672 var startOfContentLength = findBytes(stdout, bytesContentLength);
670 if (startOfContentLength >= 0) { 673 if (startOfContentLength >= 0) {
671 var newLineAfterContentLength = findBytes(stdout, 674 var newLineAfterContentLength = findBytes(stdout,
(...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after
908 * its [TestCase], starting the test process (and first, a compilation 911 * its [TestCase], starting the test process (and first, a compilation
909 * process if the TestCase is a [BrowserTestCase]), creating a timeout 912 * process if the TestCase is a [BrowserTestCase]), creating a timeout
910 * timer, and recording the results in a new [CommandOutput] object, which it 913 * timer, and recording the results in a new [CommandOutput] object, which it
911 * attaches to the TestCase. The lifetime of the RunningProcess is limited 914 * attaches to the TestCase. The lifetime of the RunningProcess is limited
912 * to the time it takes to start the process, run the process, and record 915 * to the time it takes to start the process, run the process, and record
913 * the result; there are no pointers to it, so it should be available to 916 * the result; there are no pointers to it, so it should be available to
914 * be garbage collected as soon as it is done. 917 * be garbage collected as soon as it is done.
915 */ 918 */
916 class RunningProcess { 919 class RunningProcess {
917 ProcessQueue processQueue; 920 ProcessQueue processQueue;
918 Process process; 921 io.Process process;
919 TestCase testCase; 922 TestCase testCase;
920 bool timedOut = false; 923 bool timedOut = false;
921 Date startTime; 924 Date startTime;
922 Timer timeoutTimer; 925 Timer timeoutTimer;
923 List<int> stdout; 926 List<int> stdout;
924 List<int> stderr; 927 List<int> stderr;
925 List<String> notifications; 928 List<String> notifications;
926 bool compilationSkipped; 929 bool compilationSkipped;
927 bool allowRetries; 930 bool allowRetries;
928 931
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
1046 return commandOutput; 1049 return commandOutput;
1047 } 1050 }
1048 1051
1049 void resetLocalOutputInformation() { 1052 void resetLocalOutputInformation() {
1050 stdout = new List<int>(); 1053 stdout = new List<int>();
1051 stderr = new List<int>(); 1054 stderr = new List<int>();
1052 notifications = new List<String>(); 1055 notifications = new List<String>();
1053 compilationSkipped = false; 1056 compilationSkipped = false;
1054 } 1057 }
1055 1058
1056 void drainStream(InputStream source, List<int> destination) { 1059 void drainStream(io.InputStream source, List<int> destination) {
1057 void onDataHandler () { 1060 void onDataHandler () {
1058 if (source.closed) { 1061 if (source.closed) {
1059 return; // TODO(whesse): Remove when bug is fixed. 1062 return; // TODO(whesse): Remove when bug is fixed.
1060 } 1063 }
1061 var data = source.read(); 1064 var data = source.read();
1062 while (data != null) { 1065 while (data != null) {
1063 destination.addAll(data); 1066 destination.addAll(data);
1064 data = source.read(); 1067 data = source.read();
1065 } 1068 }
1066 } 1069 }
(...skipping 14 matching lines...) Expand all
1081 commandCompleteHandler(command, returnCode); 1084 commandCompleteHandler(command, returnCode);
1082 } 1085 }
1083 1086
1084 command.outputIsUpToDate.then((bool isUpToDate) { 1087 command.outputIsUpToDate.then((bool isUpToDate) {
1085 if (isUpToDate) { 1088 if (isUpToDate) {
1086 notifications.add("Skipped compilation because the old output is " 1089 notifications.add("Skipped compilation because the old output is "
1087 "still up to date!"); 1090 "still up to date!");
1088 compilationSkipped = true; 1091 compilationSkipped = true;
1089 commandComplete(command, 0); 1092 commandComplete(command, 0);
1090 } else { 1093 } else {
1091 ProcessOptions options = new ProcessOptions(); 1094 io.ProcessOptions options = new io.ProcessOptions();
1092 if (command.environment != null) { 1095 if (command.environment != null) {
1093 options.environment = 1096 options.environment =
1094 new Map<String, String>.from(command.environment); 1097 new Map<String, String>.from(command.environment);
1095 } else { 1098 } else {
1096 options.environment = 1099 options.environment =
1097 new Map<String, String>.from(Platform.environment); 1100 new Map<String, String>.from(io.Platform.environment);
1098 } 1101 }
1099 1102
1100 options.environment['DART_CONFIGURATION'] = 1103 options.environment['DART_CONFIGURATION'] =
1101 TestUtils.configurationDir(testCase.configuration); 1104 TestUtils.configurationDir(testCase.configuration);
1102 Future processFuture = Process.start(command.executable, 1105 Future processFuture = io.Process.start(command.executable,
1103 command.arguments, 1106 command.arguments,
1104 options); 1107 options);
1105 processFuture.then((Process p) { 1108 processFuture.then((io.Process p) {
1106 process = p; 1109 process = p;
1107 process.onExit = processExitHandler; 1110 process.onExit = processExitHandler;
1108 drainStream(process.stdout, stdout); 1111 drainStream(process.stdout, stdout);
1109 drainStream(process.stderr, stderr); 1112 drainStream(process.stderr, stderr);
1110 if (timeoutTimer == null) { 1113 if (timeoutTimer == null) {
1111 // Create one timeout timer when starting test case, remove it at 1114 // Create one timeout timer when starting test case, remove it at
1112 // the end. 1115 // the end.
1113 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); 1116 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler);
1114 } 1117 }
1115 // If the timeout fired in between two commands, kill the just 1118 // If the timeout fired in between two commands, kill the just
1116 // started process immediately. 1119 // started process immediately.
1117 if (timedOut) safeKill(process); 1120 if (timedOut) safeKill(process);
1118 }); 1121 }).catchError((e) {
1119 processFuture.handleException((e) {
1120 print("Process error:"); 1122 print("Process error:");
1121 print(" Command: $command"); 1123 print(" Command: $command");
1122 print(" Error: $e"); 1124 print(" Error: $e");
1123 testComplete(createCommandOutput(command, -1, false)); 1125 testComplete(createCommandOutput(command, -1, false));
1124 return true; 1126 return true;
1125 }); 1127 });
1126 } 1128 }
1127 }); 1129 });
1128 } 1130 }
1129 1131
1130 void timeoutHandler(Timer unusedTimer) { 1132 void timeoutHandler(Timer unusedTimer) {
1131 timedOut = true; 1133 timedOut = true;
1132 safeKill(process); 1134 safeKill(process);
1133 } 1135 }
1134 1136
1135 void safeKill(Process p) { 1137 void safeKill(io.Process p) {
1136 if (p != null) { 1138 if (p != null) {
1137 try { 1139 try {
1138 p.kill(); 1140 p.kill();
1139 } on ProcessException { 1141 } on io.ProcessException {
1140 // Hopefully, this means that the process died on its own. 1142 // Hopefully, this means that the process died on its own.
1141 } 1143 }
1142 } 1144 }
1143 } 1145 }
1144 } 1146 }
1145 1147
1146 /** 1148 /**
1147 * This class holds a value, that can be changed. It is used when 1149 * This class holds a value, that can be changed. It is used when
1148 * closures need a shared value, that they can all change and read. 1150 * closures need a shared value, that they can all change and read.
1149 */ 1151 */
1150 class MutableValue<T> { 1152 class MutableValue<T> {
1151 MutableValue(T this.value); 1153 MutableValue(T this.value);
1152 T value; 1154 T value;
1153 } 1155 }
1154 1156
1155 class BatchRunnerProcess { 1157 class BatchRunnerProcess {
1156 Command _command; 1158 Command _command;
1157 String _executable; 1159 String _executable;
1158 List<String> _batchArguments; 1160 List<String> _batchArguments;
1159 1161
1160 Process _process; 1162 io.Process _process;
1161 StringInputStream _stdoutStream; 1163 io.StringInputStream _stdoutStream;
1162 StringInputStream _stderrStream; 1164 io.StringInputStream _stderrStream;
1163 1165
1164 TestCase _currentTest; 1166 TestCase _currentTest;
1165 List<int> _testStdout; 1167 List<int> _testStdout;
1166 List<int> _testStderr; 1168 List<int> _testStderr;
1167 String _status; 1169 String _status;
1168 bool _stdoutDrained = false; 1170 bool _stdoutDrained = false;
1169 bool _stderrDrained = false; 1171 bool _stderrDrained = false;
1170 MutableValue<bool> _ignoreStreams; 1172 MutableValue<bool> _ignoreStreams;
1171 Date _startTime; 1173 Date _startTime;
1172 Timer _timer; 1174 Timer _timer;
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
1291 // Move on when both stdout and stderr has been drained. 1293 // Move on when both stdout and stderr has been drained.
1292 if (_stdoutDrained) _reportResult(); 1294 if (_stdoutDrained) _reportResult();
1293 } 1295 }
1294 1296
1295 void _stdoutDone() { 1297 void _stdoutDone() {
1296 _stdoutDrained = true; 1298 _stdoutDrained = true;
1297 // Move on when both stdout and stderr has been drained. 1299 // Move on when both stdout and stderr has been drained.
1298 if (_stderrDrained) _reportResult(); 1300 if (_stderrDrained) _reportResult();
1299 } 1301 }
1300 1302
1301 void _readStdout(StringInputStream stream, List<int> buffer) { 1303 void _readStdout(io.StringInputStream stream, List<int> buffer) {
1302 var ignoreStreams = _ignoreStreams; // Capture this mutable object. 1304 var ignoreStreams = _ignoreStreams; // Capture this mutable object.
1303 void onLineHandler() { 1305 void onLineHandler() {
1304 if (ignoreStreams.value) { 1306 if (ignoreStreams.value) {
1305 while (stream.readLine() != null) { 1307 while (stream.readLine() != null) {
1306 // Do nothing. 1308 // Do nothing.
1307 } 1309 }
1308 return; 1310 return;
1309 } 1311 }
1310 // Otherwise, process output and call _reportResult() when done. 1312 // Otherwise, process output and call _reportResult() when done.
1311 var line = stream.readLine(); 1313 var line = stream.readLine();
(...skipping 10 matching lines...) Expand all
1322 line = stream.readLine(); 1324 line = stream.readLine();
1323 } 1325 }
1324 if (_status != null) { 1326 if (_status != null) {
1325 _timer.cancel(); 1327 _timer.cancel();
1326 _stdoutDone(); 1328 _stdoutDone();
1327 } 1329 }
1328 } 1330 }
1329 stream.onLine = onLineHandler; 1331 stream.onLine = onLineHandler;
1330 } 1332 }
1331 1333
1332 void _readStderr(StringInputStream stream, List<int> buffer) { 1334 void _readStderr(io.StringInputStream stream, List<int> buffer) {
1333 var ignoreStreams = _ignoreStreams; // Capture this mutable object. 1335 var ignoreStreams = _ignoreStreams; // Capture this mutable object.
1334 void onLineHandler() { 1336 void onLineHandler() {
1335 if (ignoreStreams.value) { 1337 if (ignoreStreams.value) {
1336 while (stream.readLine() != null) { 1338 while (stream.readLine() != null) {
1337 // Do nothing. 1339 // Do nothing.
1338 } 1340 }
1339 return; 1341 return;
1340 } 1342 }
1341 // Otherwise, process output and call _reportResult() when done. 1343 // Otherwise, process output and call _reportResult() when done.
1342 var line = stream.readLine(); 1344 var line = stream.readLine();
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1378 } 1380 }
1379 return handler; 1381 return handler;
1380 } 1382 }
1381 1383
1382 void _timeoutHandler(ignore) { 1384 void _timeoutHandler(ignore) {
1383 _process.onExit = makeExitHandler(">>> TEST TIMEOUT"); 1385 _process.onExit = makeExitHandler(">>> TEST TIMEOUT");
1384 _process.kill(); 1386 _process.kill();
1385 } 1387 }
1386 1388
1387 _startProcess(callback) { 1389 _startProcess(callback) {
1388 Future processFuture = Process.start(_executable, _batchArguments); 1390 Future processFuture = io.Process.start(_executable, _batchArguments);
1389 processFuture.then((Process p) { 1391 processFuture.then((io.Process p) {
1390 _process = p; 1392 _process = p;
1391 _stdoutStream = new StringInputStream(_process.stdout); 1393 _stdoutStream = new io.StringInputStream(_process.stdout);
1392 _stderrStream = new StringInputStream(_process.stderr); 1394 _stderrStream = new io.StringInputStream(_process.stderr);
1393 _process.onExit = makeExitHandler(">>> TEST CRASH"); 1395 _process.onExit = makeExitHandler(">>> TEST CRASH");
1394 callback(); 1396 callback();
1395 }); 1397 }).catchError((e) {
1396 processFuture.handleException((e) {
1397 print("Process error:"); 1398 print("Process error:");
1398 print(" Command: $_executable ${Strings.join(_batchArguments, ' ')}"); 1399 print(" Command: $_executable ${Strings.join(_batchArguments, ' ')}");
1399 print(" Error: $e"); 1400 print(" Error: $e");
1400 // If there is an error starting a batch process, chances are that 1401 // If there is an error starting a batch process, chances are that
1401 // it will always fail. So rather than re-trying a 1000+ times, we 1402 // it will always fail. So rather than re-trying a 1000+ times, we
1402 // exit. 1403 // exit.
1403 exit(1); 1404 exit(1);
1404 return true; 1405 return true;
1405 }); 1406 });
1406 } 1407 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1445 /** 1446 /**
1446 * String indicating the browser used to run the tests. Empty if no browser 1447 * String indicating the browser used to run the tests. Empty if no browser
1447 * used. 1448 * used.
1448 */ 1449 */
1449 String browserUsed = ''; 1450 String browserUsed = '';
1450 1451
1451 /** 1452 /**
1452 * Process running the selenium server .jar (only used for Safari and Opera 1453 * Process running the selenium server .jar (only used for Safari and Opera
1453 * tests.) 1454 * tests.)
1454 */ 1455 */
1455 Process _seleniumServer = null; 1456 io.Process _seleniumServer = null;
1456 1457
1457 /** True if we are in the process of starting the server. */ 1458 /** True if we are in the process of starting the server. */
1458 bool _startingServer = false; 1459 bool _startingServer = false;
1459 1460
1460 /** True if we find that there is already a selenium jar running. */ 1461 /** True if we find that there is already a selenium jar running. */
1461 bool _seleniumAlreadyRunning = false; 1462 bool _seleniumAlreadyRunning = false;
1462 1463
1463 ProcessQueue(int this._maxProcesses, 1464 ProcessQueue(int this._maxProcesses,
1464 String progress, 1465 String progress,
1465 Date startTime, 1466 Date startTime,
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
1517 if (_tests.isEmpty && _numProcesses == 0) { 1518 if (_tests.isEmpty && _numProcesses == 0) {
1518 _terminateBatchRunners().then((_) => _cleanupAndMarkDone()); 1519 _terminateBatchRunners().then((_) => _cleanupAndMarkDone());
1519 } 1520 }
1520 } 1521 }
1521 } 1522 }
1522 1523
1523 /** 1524 /**
1524 * True if we are using a browser + platform combination that needs the 1525 * True if we are using a browser + platform combination that needs the
1525 * Selenium server jar. 1526 * Selenium server jar.
1526 */ 1527 */
1527 bool get _needsSelenium => (Platform.operatingSystem == 'macos' && 1528 bool get _needsSelenium => (io.Platform.operatingSystem == 'macos' &&
1528 browserUsed == 'safari') || browserUsed == 'opera'; 1529 browserUsed == 'safari') || browserUsed == 'opera';
1529 1530
1530 /** True if the Selenium Server is ready to be used. */ 1531 /** True if the Selenium Server is ready to be used. */
1531 bool get _isSeleniumAvailable => _seleniumServer != null || 1532 bool get _isSeleniumAvailable => _seleniumServer != null ||
1532 _seleniumAlreadyRunning; 1533 _seleniumAlreadyRunning;
1533 1534
1534 /** 1535 /**
1535 * Restart all the processes that have been waiting/stopped for the server to 1536 * Restart all the processes that have been waiting/stopped for the server to
1536 * start up. If we just call this once we end up with a single-"threaded" run. 1537 * start up. If we just call this once we end up with a single-"threaded" run.
1537 */ 1538 */
1538 void resumeTesting() { 1539 void resumeTesting() {
1539 for (int i = 0; i < _maxProcesses; i++) _tryRunTest(); 1540 for (int i = 0; i < _maxProcesses; i++) _tryRunTest();
1540 } 1541 }
1541 1542
1542 /** Start the Selenium Server jar, if appropriate for this platform. */ 1543 /** Start the Selenium Server jar, if appropriate for this platform. */
1543 void _ensureSeleniumServerRunning() { 1544 void _ensureSeleniumServerRunning() {
1544 if (!_isSeleniumAvailable && !_startingServer) { 1545 if (!_isSeleniumAvailable && !_startingServer) {
1545 _startingServer = true; 1546 _startingServer = true;
1546 1547
1547 // Check to see if the jar was already running before the program started. 1548 // Check to see if the jar was already running before the program started.
1548 String cmd = 'ps'; 1549 String cmd = 'ps';
1549 var arg = ['aux']; 1550 var arg = ['aux'];
1550 if (Platform.operatingSystem == 'windows') { 1551 if (io.Platform.operatingSystem == 'windows') {
1551 cmd = 'tasklist'; 1552 cmd = 'tasklist';
1552 arg.add('/v'); 1553 arg.add('/v');
1553 } 1554 }
1554 1555
1555 Future processFuture = Process.start(cmd, arg); 1556 Future processFuture = io.Process.start(cmd, arg);
1556 processFuture.then((Process p) { 1557 processFuture.then((io.Process p) {
1557 // Drain stderr to not leak resources. 1558 // Drain stderr to not leak resources.
1558 p.stderr.onData = p.stderr.read; 1559 p.stderr.onData = p.stderr.read;
1559 final StringInputStream stdoutStringStream = 1560 final io.StringInputStream stdoutStringStream =
1560 new StringInputStream(p.stdout); 1561 new io.StringInputStream(p.stdout);
1561 stdoutStringStream.onLine = () { 1562 stdoutStringStream.onLine = () {
1562 var line = stdoutStringStream.readLine(); 1563 var line = stdoutStringStream.readLine();
1563 while (null != line) { 1564 while (null != line) {
1564 var regexp = new RegExp(r".*selenium-server-standalone.*"); 1565 var regexp = new RegExp(r".*selenium-server-standalone.*");
1565 if (regexp.hasMatch(line)) { 1566 if (regexp.hasMatch(line)) {
1566 _seleniumAlreadyRunning = true; 1567 _seleniumAlreadyRunning = true;
1567 resumeTesting(); 1568 resumeTesting();
1568 } 1569 }
1569 line = stdoutStringStream.readLine(); 1570 line = stdoutStringStream.readLine();
1570 } 1571 }
1571 if (!_isSeleniumAvailable) { 1572 if (!_isSeleniumAvailable) {
1572 _startSeleniumServer(); 1573 _startSeleniumServer();
1573 } 1574 }
1574 }; 1575 };
1575 }); 1576 }).catchError((e) {
1576 processFuture.handleException((e) {
1577 print("Error starting process:"); 1577 print("Error starting process:");
1578 print(" Command: $cmd ${Strings.join(arg, ' ')}"); 1578 print(" Command: $cmd ${Strings.join(arg, ' ')}");
1579 print(" Error: $e"); 1579 print(" Error: $e");
1580 // TODO(ahe): How to report this as a test failure? 1580 // TODO(ahe): How to report this as a test failure?
1581 exit(1); 1581 exit(1);
1582 return true; 1582 return true;
1583 }); 1583 });
1584 } 1584 }
1585 } 1585 }
1586 1586
1587 void _runTest(TestCase test) { 1587 void _runTest(TestCase test) {
1588 if (test.usesWebDriver) { 1588 if (test.usesWebDriver) {
1589 browserUsed = test.configuration['browser']; 1589 browserUsed = test.configuration['browser'];
1590 if (_needsSelenium) _ensureSeleniumServerRunning(); 1590 if (_needsSelenium) _ensureSeleniumServerRunning();
1591 } 1591 }
1592 _progress.testAdded(); 1592 _progress.testAdded();
1593 _tests.add(test); 1593 _tests.add(test);
1594 _tryRunTest(); 1594 _tryRunTest();
1595 } 1595 }
1596 1596
1597 /** 1597 /**
1598 * Monitor the output of the Selenium server, to know when we are ready to 1598 * Monitor the output of the Selenium server, to know when we are ready to
1599 * begin running tests. 1599 * begin running tests.
1600 * source: Output(Stream) from the Java server. 1600 * source: Output(Stream) from the Java server.
1601 */ 1601 */
1602 VoidFunction makeSeleniumServerHandler(StringInputStream source) { 1602 VoidFunction makeSeleniumServerHandler(io.StringInputStream source) {
1603 void handler() { 1603 void handler() {
1604 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. 1604 if (source.closed) return; // TODO(whesse): Remove when bug is fixed.
1605 var line = source.readLine(); 1605 var line = source.readLine();
1606 while (null != line) { 1606 while (null != line) {
1607 if (new RegExp(r".*Started.*Server.*").hasMatch(line) || 1607 if (new RegExp(r".*Started.*Server.*").hasMatch(line) ||
1608 new RegExp(r"Exception.*Selenium is already running.*").hasMatch( 1608 new RegExp(r"Exception.*Selenium is already running.*").hasMatch(
1609 line)) { 1609 line)) {
1610 resumeTesting(); 1610 resumeTesting();
1611 } 1611 }
1612 line = source.readLine(); 1612 line = source.readLine();
1613 } 1613 }
1614 } 1614 }
1615 return handler; 1615 return handler;
1616 } 1616 }
1617 1617
1618 /** 1618 /**
1619 * For browser tests using Safari or Opera, we need to use the Selenium 1.0 1619 * For browser tests using Safari or Opera, we need to use the Selenium 1.0
1620 * Java server. 1620 * Java server.
1621 */ 1621 */
1622 void _startSeleniumServer() { 1622 void _startSeleniumServer() {
1623 // Get the absolute path to the Selenium jar. 1623 // Get the absolute path to the Selenium jar.
1624 String filePath = TestUtils.testScriptPath; 1624 String filePath = TestUtils.testScriptPath;
1625 String pathSep = Platform.pathSeparator; 1625 String pathSep = io.Platform.pathSeparator;
1626 int index = filePath.lastIndexOf(pathSep); 1626 int index = filePath.lastIndexOf(pathSep);
1627 filePath = '${filePath.substring(0, index)}${pathSep}testing${pathSep}'; 1627 filePath = '${filePath.substring(0, index)}${pathSep}testing${pathSep}';
1628 var lister = new Directory(filePath).list(); 1628 var lister = new io.Directory(filePath).list();
1629 lister.onFile = (String file) { 1629 lister.onFile = (String file) {
1630 if (new RegExp(r"selenium-server-standalone-.*\.jar").hasMatch(file) 1630 if (new RegExp(r"selenium-server-standalone-.*\.jar").hasMatch(file)
1631 && _seleniumServer == null) { 1631 && _seleniumServer == null) {
1632 Future processFuture = Process.start('java', ['-jar', file]); 1632 Future processFuture = io.Process.start('java', ['-jar', file]);
1633 processFuture.then((Process server) { 1633 processFuture.then((io.Process server) {
1634 _seleniumServer = server; 1634 _seleniumServer = server;
1635 // Heads up: there seems to an obscure data race of some form in 1635 // Heads up: there seems to an obscure data race of some form in
1636 // the VM between launching the server process and launching the test 1636 // the VM between launching the server process and launching the test
1637 // tasks that disappears when you read IO (which is convenient, since 1637 // tasks that disappears when you read IO (which is convenient, since
1638 // that is our condition for knowing that the server is ready). 1638 // that is our condition for knowing that the server is ready).
1639 StringInputStream stdoutStringStream = 1639 io.StringInputStream stdoutStringStream =
1640 new StringInputStream(_seleniumServer.stdout); 1640 new io.StringInputStream(_seleniumServer.stdout);
1641 StringInputStream stderrStringStream = 1641 io.StringInputStream stderrStringStream =
1642 new StringInputStream(_seleniumServer.stderr); 1642 new io.StringInputStream(_seleniumServer.stderr);
1643 stdoutStringStream.onLine = 1643 stdoutStringStream.onLine =
1644 makeSeleniumServerHandler(stdoutStringStream); 1644 makeSeleniumServerHandler(stdoutStringStream);
1645 stderrStringStream.onLine = 1645 stderrStringStream.onLine =
1646 makeSeleniumServerHandler(stderrStringStream); 1646 makeSeleniumServerHandler(stderrStringStream);
1647 }); 1647 }).catchError((e) {
1648 processFuture.handleException((e) {
1649 print("Process error:"); 1648 print("Process error:");
1650 print(" Command: java -jar $file"); 1649 print(" Command: java -jar $file");
1651 print(" Error: $e"); 1650 print(" Error: $e");
1652 // TODO(ahe): How to report this as a test failure? 1651 // TODO(ahe): How to report this as a test failure?
1653 exit(1); 1652 exit(1);
1654 return true; 1653 return true;
1655 }); 1654 });
1656 } 1655 }
1657 }; 1656 };
1658 } 1657 }
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
1738 // the developer doesn't waste his or her time trying to fix a bunch of 1737 // the developer doesn't waste his or her time trying to fix a bunch of
1739 // tests that appear to be broken but were actually just flakes that 1738 // tests that appear to be broken but were actually just flakes that
1740 // didn't get retried because there had already been one failure. 1739 // didn't get retried because there had already been one failure.
1741 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; 1740 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests;
1742 new RunningProcess(test, allowRetry, this).start(); 1741 new RunningProcess(test, allowRetry, this).start();
1743 } 1742 }
1744 _numProcesses++; 1743 _numProcesses++;
1745 } 1744 }
1746 } 1745 }
1747 } 1746 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698