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

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

Powered by Google App Engine
This is Rietveld 408576698