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

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

Issue 10909240: Support for pixel layout tests. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 3 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 * The DartWrapTask generates a Dart wrapper for a test file, that has a 6 * The DartWrapTask generates a Dart wrapper for a test file, that has a
7 * test Configuration customized for the options specified by the user. 7 * test Configuration customized for the options specified by the user.
8 */ 8 */
9 class DartWrapTask extends PipelineTask { 9 class DartWrapTask extends PipelineTask {
10 final String _sourceFileTemplate; 10 final String _sourceFileTemplate;
11 final String _tempDartFileTemplate; 11 final String _tempDartFileTemplate;
12 12
13 DartWrapTask(this._sourceFileTemplate, this._tempDartFileTemplate); 13 DartWrapTask(this._sourceFileTemplate, this._tempDartFileTemplate);
14 14
15 void execute(Path testfile, List stdout, List stderr, bool logging, 15 void execute(Path testfile, List stdout, List stderr, bool logging,
16 Function exitHandler) { 16 Function exitHandler) {
17 // Get the source test file and canonicalize the path. 17 // Get the source test file and canonicalize the path.
18 var sourceName = makePathAbsolute( 18 var sourceName = makePathAbsolute(
19 expandMacros(_sourceFileTemplate, testfile)); 19 expandMacros(_sourceFileTemplate, testfile));
20 // Get the destination file. 20 // Get the destination file.
21 var destFile = expandMacros(_tempDartFileTemplate, testfile); 21 var destFile = expandMacros(_tempDartFileTemplate, testfile);
22 22
23 var p = new Path(sourceName); 23 // Get the directory that testrunner lives in; we need it to import
24 if (isLayoutRenderTest(sourceName) || config.generateRenders) { 24 // support files into the generated scripts.
25 makeLayoutTestWrapper(sourceName, destFile, p.filenameWithoutExtension); 25 var libDirectory = runnerDirectory;
Siggi Cherem (dart-lang) 2012/09/20 19:56:27 nit: seems you don't need the local variable anymo
gram 2012/09/20 20:08:17 Done.
26 exitHandler(0); 26
27 return; 27 if (config.layoutText || config.layoutPixel) {
28 makeLayoutTestWrappers(sourceName, destFile, libDirectory);
29 } else {
30 makeNonLayoutTestWrapper(sourceName, destFile, libDirectory);
28 } 31 }
32 exitHandler(0);
33 }
29 34
30 // Working buffer for the Dart wrapper. 35 void makeLayoutTestWrappers(String sourceName,
31 StringBuffer sbuf = new StringBuffer(); 36 String destFile,
37 String libDirectory) {
32 38
33 // Add the common header stuff. 39 // Get the name of the directory that has the expectation files
34 sbuf.add(directives(p.filenameWithoutExtension, 40 // (by stripping .dart suffix from test file path).
35 config.unittestPath, 41 // Create it if it does not exist.
36 sourceName)); 42 var expectedDirectory = sourceName.substring(0, sourceName.length - 5);
37 43 if (config.regenerate) {
38 // Add the test configuration and determine the action function. 44 var d = new Directory(expectedDirectory);
39 var action; 45 if (!d.existsSync()) {
40 if (config.listTests) { 46 d.createSync();
41 action = 'listTests';
42 sbuf.add(barebonesConfig());
43 sbuf.add(listTestsFunction);
44 sbuf.add(formatListMessageFunction(config.listFormat));
45 } else if (config.listGroups) {
46 sbuf.add(barebonesConfig());
47 sbuf.add(listGroupsFunction);
48 sbuf.add(formatListMessageFunction(config.listFormat));
49 action = 'listGroups';
50 } else {
51
52 if (config.runInBrowser) {
53 sbuf.add(browserTestPrintFunction);
54 sbuf.add(unblockDRTFunction);
55 } else {
56 sbuf.add(nonBrowserTestPrintFunction);
57 sbuf.add(stubUnblockDRTFunction);
58 }
59
60 if (config.runIsolated) {
61 sbuf.add(runIsolateTestsFunction);
62 action = 'runIsolateTests';
63 } else {
64 sbuf.add(runTestsFunction);
65 action = 'runTests';
66 }
67
68 sbuf.add(config.includeTime ? elapsedFunction : stubElapsedFunction);
69 sbuf.add(config.produceSummary ?
70 printSummaryFunction : stubPrintSummaryFunction);
71
72 if (config.immediateOutput) {
73 sbuf.add(printTestResultFunction);
74 sbuf.add(stubPrintAllTestResultsFunction);
75 } else {
76 sbuf.add(stubPrintTestResultFunction);
77 sbuf.add(printAllTestResultsFunction);
78 }
79
80 sbuf.add(dumpTestResultFunction);
81 sbuf.add(formatMessageFunction(config.passFormat,
82 config.failFormat,
83 config.errorFormat));
84 sbuf.add(testConfig());
85 }
86
87 // Add the filter, if applicable.
88 if (config.filtering) {
89 if (config.includeFilter.length > 0) {
90 sbuf.add(filterTestFunction(config.includeFilter, 'true'));
91 } else {
92 sbuf.add(filterTestFunction(config.excludeFilter, 'false'));
93 } 47 }
94 } 48 }
95 49
96 // Add the common trailer stuff. 50 // Create the child file that runs single tests in DRT.
97 sbuf.add(dartMain(sourceName, action, config.filtering)); 51 var childFile =
52 '${destFile.substring(0, destFile.length - 5)}-child.dart';
53 createFile(childFile, layoutTestWrapper(sourceName, libDirectory));
98 54
55 // Create the controller file that invokes DRT for each test.
56 createFile(destFile,
57 layoutTestControllerWrapper(sourceName, childFile,
58 expectedDirectory, libDirectory));
59 }
60
61 void makeNonLayoutTestWrapper(String sourceName,
62 String destFile,
63 String libDirectory) {
64 var extraImports;
65 var onDone;
66 var tprint;
67 var action;
68 if (config.runInBrowser) {
69 extraImports = "#import('dart:html');";
70 onDone = "window.postMessage('done', '*')";
71 tprint = "query('#console').addText('###\$msg\\n')";
72 } else {
73 extraImports = "#import('dart:io');";
74 onDone = "exit(e)";
75 tprint = "print('###\$msg')";
76 }
77 if (config.listTests) {
78 action = 'listTests';
79 } else if (config.listGroups) {
80 action = 'listGroups';
81 } else if (config.runIsolated) {
82 action = 'runIsolateTests';
83 } else {
84 action = 'null';
85 }
86 var wrapper = """
87 #library('layout_test');
88 $extraImports
89 #import('${config.unittestPath}', prefix:'unittest');
90 #import('$sourceName', prefix: 'test');
91 #source('$libDirectory/standard_test_runner.dart');
92
93 main() {
94 action = null;
95 immediate = ${config.immediateOutput};
96 includeTime = ${config.includeTime};
97 passFormat = '${config.passFormat}';
98 failFormat = '${config.failFormat}';
99 errorFormat = '${config.errorFormat}';
100 listFormat = '${config.listFormat}';
101 includeFilters = ${config.includeFilter};
102 excludeFilters = ${config.excludeFilter};
103 regenerate = ${config.regenerate};
104 testfile = '$sourceName';
105 summarize = ${config.produceSummary};
106 notifyDone = (e) => $onDone;
107 tprint = (msg) => $tprint;
108 action = $action;
109 runTests(test.main);
110 }
111 """;
99 // Save the Dart file. 112 // Save the Dart file.
100 createFile(destFile, sbuf.toString()); 113 createFile(destFile, wrapper);
101 exitHandler(0);
102 } 114 }
103 115
104 void cleanup(Path testfile, List stdout, List stderr, 116 void cleanup(Path testfile, List stdout, List stderr,
105 bool logging, bool keepFiles) { 117 bool logging, bool keepFiles) {
106 deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout); 118 deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout);
107 } 119 }
108 120
109 void makeLayoutTestWrapper(String sourceName, String destFile, 121 String layoutTestWrapper(String sourceName, String libDirectory) =>
110 String libraryName) { 122 """
Siggi Cherem (dart-lang) 2012/09/20 19:56:27 nit: I prefer putting this on the previous line.
gram 2012/09/20 20:08:17 Done.
111 StringBuffer sbuf = new StringBuffer(); 123 #library('layout_test');
112 var cfg = config.unittestPath.
113 replaceAll('unittest.dart', 'html_layout_config.dart');
114 sbuf.add("""
115 #library('$libraryName');
116 #import('dart:math'); 124 #import('dart:math');
117 #import('dart:isolate'); 125 #import('dart:isolate');
118 #import('dart:html'); 126 #import('dart:html');
127 #import('dart:uri');
119 #import('${config.unittestPath}', prefix:'unittest'); 128 #import('${config.unittestPath}', prefix:'unittest');
120 #import('$cfg', prefix:'unittest');
121 #import('$sourceName', prefix: 'test'); 129 #import('$sourceName', prefix: 'test');
122 """); 130 #source('$libDirectory/layout_test_runner.dart');
123 // Add the filter, if applicable. 131
124 if (config.filtering) {
125 if (config.includeFilter.length > 0) {
126 sbuf.add(filterTestFunction(config.includeFilter, 'true'));
127 } else {
128 sbuf.add(filterTestFunction(config.excludeFilter, 'false'));
129 }
130 }
131 sbuf.add("""
132 main() { 132 main() {
133 unittest.groupSep = '###'; 133 includeFilters = ${config.includeFilter};
134 unittest.useHtmlLayoutConfiguration(); 134 excludeFilters = ${config.excludeFilter};
135 unittest.group('', test.main); 135 runTests(test.main);
136 ${config.filtering ? 'unittest.filterTests(filterTest);' : ''} 136 }
137 if (window.location.search == '') unittest.runTests(); 137 """;
138 }
139 """);
140 // Save the Dart file.
141 createFile(destFile, sbuf.toString());
142 }
143 138
144 String directives(String library, String unittest, String sourceName) { 139
140 String layoutTestControllerWrapper(String sourceName, String childName,
141 String expectedDirectory,
142 String libDirectory) {
143 StringBuffer sbuf = new StringBuffer();
144 var htmlFile = childName.replaceFirst('-child.dart', '.html');
145 // Add common prefix.
145 return """ 146 return """
146 #library('$library'); 147 #library('layout_controller');
148 #import('dart:uri');
149 #import('dart:io');
147 #import('dart:math'); 150 #import('dart:math');
148 #import('dart:isolate'); 151 #source('$libDirectory/layout_test_controller.dart');
149 #import('$unittest', prefix:'unittest');
150 #import('$sourceName', prefix: 'test');
151 """;
152 }
153 152
154 // The core skeleton for a config. Most of the guts is in the 153 main() {
155 // parameter [body]. 154 includeTime = ${config.includeTime};
156 String configuration([String body = '']) { 155 passFormat = '${config.passFormat}';
157 return """ 156 failFormat = '${config.failFormat}';
158 class TestRunnerConfiguration extends unittest.Configuration { 157 errorFormat = '${config.errorFormat}';
159 get name => 'Test runner configuration'; 158 listFormat = '${config.listFormat}';
160 get autoStart => false; 159 drt = '${makePathAbsolute(config.drtPath)}';
161 $body 160 regenerate = ${config.regenerate};
161 sourceDir = '$expectedDirectory';
162 testfile = '$sourceName';
163 summarize = ${config.produceSummary};
164 baseUrl = 'file://$htmlFile';
165 run${config.layoutText?'Text':'Pixel'}LayoutTest(0);
162 } 166 }
163 """; 167 """;
164 } 168 }
165
166 // A barebones config, used for listing tests, not running them.
167 String barebonesConfig() {
168 return configuration();
169 }
170
171 // A more complex config, used for running tests.
172 String testConfig() {
173 return configuration("""
174 void onTestResult(unittest.TestCase testCase) {
175 printResult('\$testFile ', testCase);
176 }
177
178 void onDone(int passed, int failed, int errors,
179 List<unittest.TestCase> results,
180 String uncaughtError) {
181 var success = (passed > 0 && failed == 0 && errors == 0 &&
182 uncaughtError == null);
183 printResults(testFile, results);
184 printSummary(testFile, passed, failed, errors, uncaughtError);
185 unblockDRT();
186 }
187 """);
188 }
189
190 // The main function, that creates the config, filters the tests if
191 // necessary, then performs the action (list/run/run-isolated).
192 String dartMain(String sourceName, String action, bool filter) {
193 return """
194 var testFile = '$sourceName';
195 main() {
196 unittest.groupSep = '###';
197 unittest.configure(new TestRunnerConfiguration());
198 unittest.group('', test.main);
199 ${filter ? 'unittest.filterTests(filterTest);' : ''}
200 $action();
201 } 169 }
202 """;
203 }
204
205 // For 'printing' when we are in the browser, we add text elements
206 // to a DOM element with id 'console'.
207 final String browserTestPrintFunction = """
208 #import('dart:html');
209 void tprint(msg) {
210 var pre = query('#console');
211 pre.addText('###\$msg\\n');
212 }
213 """;
214
215 // For printing when not in the browser we can just use Dart's print().
216 final String nonBrowserTestPrintFunction = """
217 void tprint(msg) {
218 print('###\$msg');
219 }
220 """;
221
222 // A function to give us the elapsed time for a test.
223 final String elapsedFunction = """
224 String elapsed(unittest.TestCase t) {
225 double duration = t.runningTime.inMilliseconds.toDouble();
226 duration /= 1000;
227 return '\${duration.toStringAsFixed(3)}s ';
228 }
229 """;
230
231 // A dummy version of the elapsed function for when the user
232 // doesn't want test times included.
233 final String stubElapsedFunction = """
234 String elapsed(unittest.TestCase t) {
235 return '';
236 }
237 """;
238
239 // A function to print the results of a test.
240 final String dumpTestResultFunction = """
241 void dumpTestResult(source, unittest.TestCase t) {
242 var groupName = '', testName = '';
243 var idx = t.description.lastIndexOf('###');
244 if (idx >= 0) {
245 groupName = t.description.substring(0, idx).replaceAll('###', ' ');
246 testName = t.description.substring(idx+3);
247 } else {
248 testName = t.description;
249 }
250 var stack = (t.stackTrace == null) ? '' : '\${t.stackTrace} ';
251 var message = (t.message.length > 0) ? '\$t.message ' : '';
252 var duration = elapsed(t);
253 tprint(formatMessage(source, '\$groupName ', '\$testName ',
254 duration, t.result, message, stack));
255 }
256 """;
257
258 // A function to print the test summary.
259 final String printSummaryFunction = """
260 void printSummary(String testFile, int passed, int failed, int errors,
261 String uncaughtError) {
262 tprint('');
263 if (passed == 0 && failed == 0 && errors == 0) {
264 tprint('\$testFile: No tests found.');
265 } else if (failed == 0 && errors == 0 && uncaughtError == null) {
266 tprint('\$testFile: All \$passed tests passed.');
267 } else {
268 if (uncaughtError != null) {
269 tprint('\$testFile: Top-level uncaught error: \$uncaughtError');
270 }
271 tprint('\$testFile: \$passed PASSED, \$failed FAILED, \$errors ERRORS');
272 }
273 }
274 """;
275
276 final String stubPrintSummaryFunction = """
277 void printSummary(String testFile, int passed, int failed, int errors,
278 String uncaughtError) {
279 }
280 """;
281
282 // A function to print all test results.
283 final String printAllTestResultsFunction = """
284 void printResults(testfile, List<unittest.TestCase> results) {
285 for (final testCase in results) {
286 dumpTestResult('\$testfile ', testCase);
287 }
288 }
289 """;
290
291 final String stubPrintAllTestResultsFunction = """
292 void printResults(testfile, List<unittest.TestCase> results) {
293 }
294 """;
295
296 // A function to print a single test result.
297 final String printTestResultFunction = """
298 void printResult(testfile, unittest.TestCase testCase) {
299 dumpTestResult('\$testfile ', testCase);
300 }
301 """;
302
303 final String stubPrintTestResultFunction = """
304 void printResult(testfile, unittest.TestCase testCase) {
305 }
306 """;
307
308 final String unblockDRTFunction = """
309 void unblockDRT() {
310 window.postMessage('done', '*');
311 }
312 """;
313
314 final String stubUnblockDRTFunction = """
315 void unblockDRT() {
316 }
317 """;
318
319 // A simple format function for listing tests.
320 String formatListMessageFunction(String format) {
321 return """
322 String formatMessage(filename, groupname, [ testname = '']) {
323 return '${format}'.
324 replaceAll('${Macros.testfile}', filename).
325 replaceAll('${Macros.testGroup}', groupname).
326 replaceAll('${Macros.testDescription}', testname);
327 }
328 """;
329 }
330
331 // A richer format function for test results.
332 String formatMessageFunction(
333 String passFormat, String failFormat, String errorFormat) {
334 return """
335 String formatMessage(filename, groupname,
336 [ testname = '', testTime = '', result = '',
337 message = '', stack = '' ]) {
338 var format = '$errorFormat';
339 if (result == 'pass') format = '$passFormat';
340 else if (result == 'fail') format = '$failFormat';
341 return format.
342 replaceAll('${Macros.testTime}', testTime).
343 replaceAll('${Macros.testfile}', filename).
344 replaceAll('${Macros.testGroup}', groupname).
345 replaceAll('${Macros.testDescription}', testname).
346 replaceAll('${Macros.testMessage}', message).
347 replaceAll('${Macros.testStacktrace}', stack);
348 }
349 """;
350 }
351
352 // A function to list the test groups.
353 final String listGroupsFunction = """
354 listGroups() {
355 List tests = unittest.testCases;
356 Map groups = {};
357 for (var t in tests) {
358 var groupName, testName = '';
359 var idx = t.description.lastIndexOf('###');
360 if (idx >= 0) {
361 groupName = t.description.substring(0, idx).replaceAll('###', ' ');
362 if (!groups.containsKey(groupName)) {
363 groups[groupName] = '';
364 }
365 }
366 }
367 for (var g in groups.getKeys()) {
368 var msg = formatMessage('\$testfile ', '\$g ');
369 print('###\$msg');
370 }
371 }
372 """;
373
374 // A function to list the tests.
375 final String listTestsFunction = """
376 listTests() {
377 List tests = unittest.testCases;
378 for (var t in tests) {
379 var groupName, testName = '';
380 var idx = t.description.lastIndexOf('###');
381 if (idx >= 0) {
382 groupName = t.description.substring(0, idx).replaceAll('###', ' ');
383 testName = t.description.substring(idx+3);
384 } else {
385 groupName = '';
386 testName = t.description;
387 }
388 var msg = formatMessage('\$testfile ', '\$groupName ', '\$testName ');
389 print('###\$msg');
390 }
391 }
392 """;
393
394 // A function to filter the tests.
395 String filterTestFunction(List filters, String filterReturnValue) {
396 StringBuffer sbuf = new StringBuffer();
397 sbuf.add('filterTest(t) {\n');
398 if (filters != null) {
399 sbuf.add(' var name = t.description.replaceAll("###", " ");\n');
400 for (var f in filters) {
401 sbuf.add(' if (name.indexOf("$f")>=0) return $filterReturnValue;\n');
402 }
403 sbuf.add(' return !$filterReturnValue;\n');
404 } else {
405 sbuf.add(' return true;\n');
406 }
407 sbuf.add('}\n');
408 return sbuf.toString();
409 }
410
411 // Code to support running single tests in isolates.
412 final String runIsolateTestsFunction = """
413 class TestRunnerChildConfiguration extends unittest.Configuration {
414 get name => 'Test runner child configuration';
415 get autoStart => false;
416
417 void onDone(int passed, int failed, int errors,
418 List<unittest.TestCase> results, String uncaughtError) {
419 unittest.TestCase test = results[0];
420 parentPort.send([test.result, test.runningTime.inMilliseconds,
421 test.message, test.stackTrace]);
422 }
423 }
424
425 var parentPort;
426 runChildTest() {
427 port.receive((testName, sendport) {
428 parentPort = sendport;
429 unittest.configure(new TestRunnerChildConfiguration());
430 unittest.groupSep = '###';
431 unittest.group('', test.main);
432 unittest.filterTests(testName);
433 unittest.runTests();
434 });
435 }
436
437 var testNum;
438 var failed;
439 var errors;
440 var passed;
441
442 runParentTest() {
443 var tests = unittest.testCases;
444 tests[testNum].startTime = new Date.now();
445 SendPort childPort = spawnFunction(runChildTest);
446 childPort.call(tests[testNum].description).then((results) {
447 var result = results[0];
448 var duration = new Duration(milliseconds: results[1]);
449 var message = results[2];
450 var stack = results[3];
451 if (result == 'pass') {
452 tests[testNum].pass();
453 ++passed;
454 } else if (result == 'fail') {
455 tests[testNum].fail(message, stack);
456 ++failed;
457 } else {
458 tests[testNum].error(message, stack);
459 ++errors;
460 }
461 tests[testNum].runningTime = duration;
462 ++testNum;
463 if (testNum < tests.length) {
464 runParentTest();
465 } else {
466 unittest.config.onDone(passed, failed, errors,
467 unittest.testCases, null);
468 }
469 });
470 }
471
472 runIsolateTests() {
473 testNum = 0;
474 passed = failed = errors = 0;
475 runParentTest();
476 }
477 """;
478
479 // Code for running all tests in the normal (non-isolate) way.
480 final String runTestsFunction = """
481 runTests() {
482 unittest.runTests();
483 }
484 """;
485
486 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698