OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // The following set of variables should be set by the caller that | 5 // The following set of variables should be set by the caller that |
6 // #sources this file. | 6 // #sources this file. |
7 /** Whether to include elapsed time. */ | 7 /** Whether to include elapsed time. */ |
8 | 8 |
9 part of test_controller; | 9 part of test_controller; |
10 | 10 |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
117 } | 117 } |
118 } | 118 } |
119 | 119 |
120 complete() { | 120 complete() { |
121 if (summarize) { | 121 if (summarize) { |
122 printSummary(testfile, passCount, failCount, errorCount); | 122 printSummary(testfile, passCount, failCount, errorCount); |
123 } | 123 } |
124 notifyDone(failCount > 0 ? -1 : 0); | 124 notifyDone(failCount > 0 ? -1 : 0); |
125 } | 125 } |
126 | 126 |
127 /* | |
128 * Run an external process [cmd] with command line arguments [args]. | |
129 * [timeout] can be used to forcefully terminate the process after | |
130 * some number of seconds. This is used by runCommand and startProcess. | |
131 * If [procId] is non-zero (i.e. called from startProcess) then a reference | |
132 * to the [Process] will be put in a map with key [procId]; in this case | |
133 * the process can be terminated later by calling [stopProcess] and | |
134 * passing in the [procId]. | |
135 * [outputMonitor] is an optional function that will be called back with each | |
136 * line of output from the process. | |
137 * Returns a [Future] for when the process terminates. | |
138 */ | |
139 Future _processHelper(String command, List<String> args, | |
140 List stdout, List stderr, | |
141 int timeout, int procId, Function outputMonitor, bool raw) { | |
142 var completer = procId == 0 ? (new Completer()) : null; | |
143 var timer = null; | |
144 var processFuture = Process.start(command, args); | |
145 processFuture.then((process) { | |
146 | |
147 timer = new Timer(new Duration(seconds: timeout), () { | |
148 timer = null; | |
149 process.kill(); | |
150 }); | |
151 | |
152 process.exitCode.then((exitCode) { | |
153 if (timer != null) { | |
154 timer.cancel(); | |
155 } | |
156 if (completer != null) { | |
157 completer.complete(exitCode); | |
Siggi Cherem (dart-lang)
2013/04/19 21:39:39
can we get rid of using completer?
I think it sho
gram
2013/04/22 23:54:27
Done.
| |
158 } | |
159 }); | |
160 | |
161 if (raw) { | |
162 process.stdout.listen((c) { stdout.addAll(c); }); | |
163 } else { | |
164 _pipeStream(process.stdout, stdout, outputMonitor); | |
165 } | |
166 _pipeStream(process.stderr, stderr, outputMonitor); | |
167 }) | |
168 .catchError((e) { | |
169 stderr.add("#Error starting process $command: ${e.error}"); | |
170 }); | |
171 | |
172 return completer.future; | |
Siggi Cherem (dart-lang)
2013/04/19 21:39:39
wouldn't this crash when procId != 0 (since comple
gram
2013/04/22 23:54:27
Yes - but thankfully there is no completer now :-)
| |
173 } | |
174 | |
175 void _pipeStream(Stream stream, List<String> destination, | |
176 Function outputMonitor) { | |
177 stream | |
178 .transform(new StringDecoder()) | |
179 .transform(new LineTransformer()) | |
180 .listen((String line) { | |
181 if (outputMonitor != null) { | |
182 outputMonitor(line); | |
183 } | |
184 destination.add(line); | |
185 }); | |
186 } | |
187 | |
188 /** | |
189 * Run an external process [cmd] with command line arguments [args]. | |
190 * [timeout] can be used to forcefully terminate the process after | |
191 * some number of seconds. | |
192 * Returns a [Future] for when the process terminates. | |
193 */ | |
194 Future runCommand(String command, List<String> args, | |
195 List stdout, List stderr, | |
196 {int timeout: 300, Function outputMonitor, | |
197 bool raw: false}) { | |
198 return _processHelper(command, args, stdout, stderr, | |
199 timeout, 0, outputMonitor, raw); | |
200 } | |
201 | |
202 String parseLabel(String line) { | |
203 if (line.startsWith('CONSOLE MESSAGE')) { | |
204 var idx = line.indexOf('#TEST '); | |
205 if (idx > 0) { | |
206 return line.substring(idx + 6); | |
207 } | |
208 } | |
209 return null; | |
210 } | |
211 | |
127 runTextLayoutTest(testNum) { | 212 runTextLayoutTest(testNum) { |
128 var url = '$baseUrl?test=$testNum'; | 213 var url = '$baseUrl?test=$testNum'; |
129 var stdout = new List(); | 214 var stdout = new List(); |
215 var stderr = new List(); | |
130 start = new DateTime.now(); | 216 start = new DateTime.now(); |
131 Process.start(drt, [url]).then((process) { | 217 runCommand(drt, [url], stdout, stderr).then((e) { |
132 // Drain stderr to not leak resources. | 218 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { |
133 process.stderr.onData = process.stderr.read; | 219 stdout.removeLast(); |
134 StringInputStream stdoutStringStream = | 220 } |
135 new StringInputStream(process.stdout); | 221 var done = false; |
136 stdoutStringStream.onLine = () { | 222 var i = 0; |
137 if (stdoutStringStream.closed) return; | 223 var label = null; |
138 var line = stdoutStringStream.readLine(); | 224 var contentMarker = 'layer at '; |
139 while (null != line) { | 225 while (i < stdout.length) { |
140 stdout.add(line); | 226 if (label == null && (label = parseLabel(stdout[i])) != null) { |
141 line = stdoutStringStream.readLine(); | 227 if (label == 'NONEXISTENT') { |
142 } | 228 complete(); |
143 }; | 229 return; |
144 process.onExit = (exitCode) { | 230 } |
145 process.close(); | 231 } else if (stdout[i].startsWith(contentMarker)) { |
146 if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) { | 232 if (label == null) { |
147 stdout.removeLast(); | 233 complete(); |
148 } | 234 return; |
149 var done = false; | 235 } |
150 var i = 0; | 236 var expectedFileName = |
151 var label = null; | 237 '$sourceDir${Platform.pathSeparator}' |
152 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | 238 '${label.replaceAll("###", "_") |
153 var contentMarker = 'layer at '; | 239 .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt'; |
154 while (i < stdout.length) { | 240 var expected = new File(expectedFileName); |
155 if (label == null && stdout[i].startsWith(labelMarker)) { | 241 if (regenerate) { |
156 label = stdout[i].substring(labelMarker.length); | 242 var osink = expected.openWrite(); |
157 if (label == 'NONEXISTENT') { | 243 while (i < stdout.length) { |
158 complete(); | 244 osink.write(stdout[i]); |
159 } | 245 osink.write('\n'); |
160 } else if (stdout[i].startsWith(contentMarker)) { | 246 i++; |
161 if (label == null) { | 247 } |
162 complete(); | 248 osink.close(); |
163 } | 249 pass(start, label); |
164 var expectedFileName = | 250 } else if (!expected.existsSync()) { |
165 '$sourceDir${Platform.pathSeparator}' | 251 fail(start, label, 'No expectation file'); |
166 '${label.replaceAll("###", "_") | 252 } else { |
167 .replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt'; | 253 var lines = expected.readAsLinesSync(); |
168 var expected = new File(expectedFileName); | 254 var actualLength = stdout.length - i; |
169 if (regenerate) { | 255 var compareCount = min(lines.length, actualLength); |
170 var ostream = expected.openOutputStream(FileMode.WRITE); | 256 var match = true; |
171 while (i < stdout.length) { | 257 for (var j = 0; j < compareCount; j++) { |
172 ostream.writeString(stdout[i]); | 258 if (lines[j] != stdout[i + j]) { |
173 ostream.writeString('\n'); | 259 fail(start, label, 'Expectation differs at line ${j + 1}'); |
174 i++; | 260 match = false; |
261 break; | |
175 } | 262 } |
176 ostream.close(); | 263 } |
177 pass(start, label); | 264 if (match) { |
178 } else if (!expected.existsSync()) { | 265 if (lines.length != actualLength) { |
179 fail(start, label, 'No expectation file'); | 266 fail(start, label, 'Expectation file has wrong length'); |
267 } else { | |
268 pass(start, label); | |
269 } | |
270 } | |
271 } | |
272 done = true; | |
273 break; | |
274 } | |
275 i++; | |
276 } | |
277 if (label != null) { | |
278 if (!done) error(start, label, 'Failed to parse output'); | |
279 runTextLayoutTest(testNum + 1); | |
280 } | |
281 }); | |
282 } | |
283 | |
284 runPixelLayoutTest(int testNum) { | |
285 var url = '$baseUrl?test=$testNum'; | |
286 var stdout = new List(); | |
287 var stderr = new List(); | |
288 start = new DateTime.now(); | |
289 runCommand(drt, ["$url'-p"], stdout, stderr, raw:true).then((exitCode) { | |
290 var contentMarker = 'Content-Length: '; | |
291 var eol = '\n'.codeUnitAt(0); | |
292 var pos = 0; | |
293 var label = null; | |
294 var done = false; | |
295 | |
296 while(pos < stdout.length) { | |
297 StringBuffer sb = new StringBuffer(); | |
298 while (pos < stdout.length && stdout[pos] != eol) { | |
299 sb.writeCharCode(stdout[pos++]); | |
300 } | |
301 if (++pos >= stdout.length && line == '') break; | |
302 var line = sb.toString(); | |
303 | |
304 if (label == null && (label = parseLabel(line)) != null) { | |
305 if (label == 'NONEXISTENT') { | |
306 complete(); | |
307 } | |
308 } else if (line.startsWith(contentMarker)) { | |
309 if (label == null) { | |
310 complete(); | |
311 } | |
312 var len = int.parse(line.substring(contentMarker.length)); | |
313 var expectedFileName = | |
314 '$sourceDir${Platform.pathSeparator}' | |
315 '${label.replaceAll("###","_"). | |
316 replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png'; | |
317 var expected = new File(expectedFileName); | |
318 if (regenerate) { | |
319 var osink = expected.openWrite(); | |
320 stdout.removeRange(0, pos); | |
321 stdout.length = len; | |
322 osink.add(stdout); | |
323 osink.close(); | |
324 pass(start, label); | |
325 } else if (!expected.existsSync()) { | |
326 fail(start, label, 'No expectation file'); | |
327 } else { | |
328 var bytes = expected.readAsBytesSync(); | |
329 if (bytes.length != len) { | |
330 fail(start, label, 'Expectation file has wrong length'); | |
180 } else { | 331 } else { |
181 var lines = expected.readAsLinesSync(); | |
182 var actualLength = stdout.length - i; | |
183 var compareCount = min(lines.length, actualLength); | |
184 var match = true; | 332 var match = true; |
185 for (var j = 0; j < compareCount; j++) { | 333 for (var j = 0; j < len; j++) { |
186 if (lines[j] != stdout[i + j]) { | 334 if (bytes[j] != stdout[pos + j]) { |
187 fail(start, label, 'Expectation differs at line ${j + 1}'); | 335 fail(start, label, 'Expectation differs at byte ${j + 1}'); |
188 match = false; | 336 match = false; |
189 break; | 337 break; |
190 } | 338 } |
191 } | 339 } |
192 if (match) { | 340 if (match) pass(start, label); |
193 if (lines.length != actualLength) { | 341 } |
194 fail(start, label, 'Expectation file has wrong length'); | 342 } |
195 } else { | 343 done = true; |
196 pass(start, label); | 344 break; |
197 } | 345 } |
198 } | 346 } |
199 } | 347 if (label != null) { |
200 done = true; | 348 if (!done) error(start, label, 'Failed to parse output'); |
201 break; | 349 runPixelLayoutTest(testNum + 1); |
202 } | 350 } |
203 i++; | |
204 } | |
205 if (label != null) { | |
206 if (!done) error(start, label, 'Failed to parse output'); | |
207 runTextLayoutTest(testNum + 1); | |
208 } | |
209 }; | |
210 }); | 351 }); |
211 } | 352 } |
212 | 353 |
213 runPixelLayoutTest(int testNum) { | |
214 var url = '$baseUrl?test=$testNum'; | |
215 var stdout = new List(); | |
216 start = new DateTime.now(); | |
217 Process.start(drt, ["$url'-p"]).then((process) { | |
218 // Drain stderr to not leak resources. | |
219 process.stderr.onData = process.stderr.read; | |
220 ListInputStream stdoutStream = process.stdout; | |
221 stdoutStream.onData = () { | |
222 if (!stdoutStream.closed) { | |
223 var data = stdoutStream.read(); | |
224 stdout.addAll(data); | |
225 } | |
226 }; | |
227 stdoutStream.onError = (e) { | |
228 print(e); | |
229 }; | |
230 process.onExit = (exitCode) { | |
231 stdout.addAll(process.stdout.read()); | |
232 process.close(); | |
233 var labelMarker = 'CONSOLE MESSAGE: #TEST '; | |
234 var contentMarker = 'Content-Length: '; | |
235 var eol = '\n'.codeUnitAt(0); | |
236 var pos = -1; | |
237 var label = null; | |
238 var done = false; | |
239 | |
240 while(pos < stdout.length) { | |
241 var idx = stdout.indexOf(eol, ++pos); | |
242 if (idx < 0) break; | |
243 StringBuffer sb = new StringBuffer(); | |
244 for (var i = pos; i < idx; i++) { | |
245 sb.writeCharCode(stdout[i]); | |
246 } | |
247 var line = sb.toString(); | |
248 | |
249 if (label == null && line.startsWith(labelMarker)) { | |
250 label = line.substring(labelMarker.length); | |
251 if (label == 'NONEXISTENT') { | |
252 complete(); | |
253 } | |
254 } else if (line.startsWith(contentMarker)) { | |
255 if (label == null) { | |
256 complete(); | |
257 } | |
258 var len = int.parse(line.substring(contentMarker.length)); | |
259 pos = idx + 1; | |
260 var expectedFileName = | |
261 '$sourceDir${Platform.pathSeparator}' | |
262 '${label.replaceAll("###","_"). | |
263 replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png'; | |
264 var expected = new File(expectedFileName); | |
265 if (regenerate) { | |
266 var ostream = expected.openOutputStream(FileMode.WRITE); | |
267 ostream.writeFrom(stdout, pos, len); | |
268 ostream.close(); | |
269 pass(start, label); | |
270 } else if (!expected.existsSync()) { | |
271 fail(start, label, 'No expectation file'); | |
272 } else { | |
273 var bytes = expected.readAsBytesSync(); | |
274 if (bytes.length != len) { | |
275 fail(start, label, 'Expectation file has wrong length'); | |
276 } else { | |
277 var match = true; | |
278 for (var j = 0; j < len; j++) { | |
279 if (bytes[j] != stdout[pos + j]) { | |
280 fail(start, label, 'Expectation differs at byte ${j + 1}'); | |
281 match = false; | |
282 break; | |
283 } | |
284 } | |
285 if (match) pass(start, label); | |
286 } | |
287 } | |
288 done = true; | |
289 break; | |
290 } | |
291 pos = idx; | |
292 } | |
293 if (label != null) { | |
294 if (!done) error(start, label, 'Failed to parse output'); | |
295 runPixelLayoutTest(testNum + 1); | |
296 } | |
297 }; | |
298 }); | |
299 } | |
300 | |
301 void init() { | 354 void init() { |
302 // Get the name of the directory that has the expectation files | 355 // Get the name of the directory that has the expectation files |
303 // (by stripping .dart suffix from test file path). | 356 // (by stripping .dart suffix from test file path). |
304 // Create it if it does not exist. | 357 // Create it if it does not exist. |
305 sourceDir = testfile.substring(0, testfile.length - 5); | 358 sourceDir = testfile.substring(0, testfile.length - 5); |
306 if (regenerate) { | 359 if (regenerate) { |
307 var d = new Directory(sourceDir); | 360 var d = new Directory(sourceDir); |
308 if (!d.existsSync()) { | 361 if (!d.existsSync()) { |
309 d.createSync(); | 362 d.createSync(); |
310 } | 363 } |
311 } | 364 } |
312 } | 365 } |
313 | 366 |
314 void runPixelLayoutTests() { | 367 void runPixelLayoutTests() { |
315 init(); | 368 init(); |
316 runPixelLayoutTest(0); | 369 runPixelLayoutTest(0); |
317 } | 370 } |
318 | 371 |
319 void runTextLayoutTests() { | 372 void runTextLayoutTests() { |
320 init(); | 373 init(); |
321 runTextLayoutTest(0); | 374 runTextLayoutTest(0); |
322 } | 375 } |
OLD | NEW |