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

Side by Side Diff: utils/testrunner/layout_test_controller.dart

Issue 14247033: Updated testrunner: (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 8 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 // 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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698