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

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 = getRunnerDirectory();
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 }
34
35 void makeLayoutTestWrappers(String sourceName,
36 String destFile,
37 String libDirectory) {
38
39 // Get the name of the directory that has the expectation files
40 // (by stripping .dart suffix from test file path).
41 // Create it if it does not exist.
42 var expectedDirectory = sourceName.substring(0, sourceName.length - 5);
43 if (config.regenerate) {
44 var d = new Directory(expectedDirectory);
45 if (!d.existsSync()) {
46 d.createSync();
47 }
48 }
49
50 // Create the child file that runs single tests in DRT.
51 var childFile =
52 '${destFile.substring(0, destFile.length - 5)}-child.dart';
53 createFile(childFile, layoutTestWrapper(sourceName, libDirectory));
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) {
29 64
30 // Working buffer for the Dart wrapper. 65 // Working buffer for the Dart wrapper.
31 StringBuffer sbuf = new StringBuffer(); 66 StringBuffer sbuf = new StringBuffer();
32 67
33 // Add the common header stuff. 68 sbuf.add("""
34 sbuf.add(directives(p.filenameWithoutExtension, 69 #library('layout_test');
35 config.unittestPath, 70 """);
36 sourceName)); 71 if (config.runInBrowser) {
Siggi Cherem (dart-lang) 2012/09/20 17:37:27 it would be nice to pull out this and the other 'i
gram 2012/09/20 18:58:03 Done.
72 sbuf.add("#import('dart:html');\n");
73 } else {
74 sbuf.add("#import('dart:io');\n");
75 }
76 sbuf.add("""
77 #import('${config.unittestPath}', prefix:'unittest');
78 #import('$sourceName', prefix: 'test');
79 #source('$libDirectory/non_layout_test_runner.dart');
37 80
38 // Add the test configuration and determine the action function. 81 main() {
39 var action; 82 action = null;
83 immediate = ${config.immediateOutput};
84 includeTime = ${config.includeTime};
85 passFormat = '${config.passFormat}';
86 failFormat = '${config.failFormat}';
87 errorFormat = '${config.errorFormat}';
88 listFormat = '${config.listFormat}';
89 includeFilters = ${config.includeFilter};
90 excludeFilters = ${config.excludeFilter};
91 regenerate = ${config.regenerate};
92 testfile = '$sourceName';
93 summarize = ${config.produceSummary};
94 """);
95 if (config.runInBrowser) {
96 sbuf.add("""
97 notifyDone = (e) => window.postMessage("done", "*");
98 tprint = (msg) => query("#console").addText("###\$msg\\n");
99 """);
100 } else {
101 sbuf.add("""
102 notifyDone = (e) => exit(e);
103 tprint = (msg) => print("###\$msg");
104 """);
105 }
40 if (config.listTests) { 106 if (config.listTests) {
41 action = 'listTests'; 107 sbuf.add(' action = listTests;\n');
42 sbuf.add(barebonesConfig());
43 sbuf.add(listTestsFunction);
44 sbuf.add(formatListMessageFunction(config.listFormat));
45 } else if (config.listGroups) { 108 } else if (config.listGroups) {
46 sbuf.add(barebonesConfig()); 109 sbuf.add(' action = listGroups;\n');
47 sbuf.add(listGroupsFunction); 110 } else if (config.runIsolated) {
48 sbuf.add(formatListMessageFunction(config.listFormat)); 111 sbuf.add(' action = runIsolateTests;\n');
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 } 112 }
86 113 sbuf.add("""
87 // Add the filter, if applicable. 114 runTests(test.main);
88 if (config.filtering) { 115 }
89 if (config.includeFilter.length > 0) { 116 """);
90 sbuf.add(filterTestFunction(config.includeFilter, 'true'));
91 } else {
92 sbuf.add(filterTestFunction(config.excludeFilter, 'false'));
93 }
94 }
95
96 // Add the common trailer stuff.
97 sbuf.add(dartMain(sourceName, action, config.filtering));
98
99 // Save the Dart file. 117 // Save the Dart file.
100 createFile(destFile, sbuf.toString()); 118 createFile(destFile, sbuf.toString());
101 exitHandler(0);
102 } 119 }
103 120
104 void cleanup(Path testfile, List stdout, List stderr, 121 void cleanup(Path testfile, List stdout, List stderr,
105 bool logging, bool keepFiles) { 122 bool logging, bool keepFiles) {
106 deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout); 123 deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout);
107 } 124 }
108 125
109 void makeLayoutTestWrapper(String sourceName, String destFile, 126 String layoutTestWrapper(String sourceName, String libDirectory) {
110 String libraryName) {
111 StringBuffer sbuf = new StringBuffer(); 127 StringBuffer sbuf = new StringBuffer();
112 var cfg = config.unittestPath.
113 replaceAll('unittest.dart', 'html_layout_config.dart');
114 sbuf.add(""" 128 sbuf.add("""
Siggi Cherem (dart-lang) 2012/09/20 17:37:27 seems like we can get rid of the buffer and return
gram 2012/09/20 18:58:03 Done.
115 #library('$libraryName'); 129 #library('layout_test');
116 #import('dart:math'); 130 #import('dart:math');
117 #import('dart:isolate'); 131 #import('dart:isolate');
118 #import('dart:html'); 132 #import('dart:html');
133 #import('dart:uri');
119 #import('${config.unittestPath}', prefix:'unittest'); 134 #import('${config.unittestPath}', prefix:'unittest');
120 #import('$cfg', prefix:'unittest');
121 #import('$sourceName', prefix: 'test'); 135 #import('$sourceName', prefix: 'test');
136 #source('$libDirectory/layout_test_runner.dart');
137
138 main() {
139 includeFilters = ${config.includeFilter};
140 excludeFilters = ${config.excludeFilter};
141 runTests(test.main);
142 }
122 """); 143 """);
123 // Add the filter, if applicable.
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() {
133 unittest.groupSep = '###';
134 unittest.useHtmlLayoutConfiguration();
135 unittest.group('', test.main);
136 ${config.filtering ? 'unittest.filterTests(filterTest);' : ''}
137 if (window.location.search == '') unittest.runTests();
138 }
139 """);
140 // Save the Dart file.
141 createFile(destFile, sbuf.toString());
142 }
143
144 String directives(String library, String unittest, String sourceName) {
145 return """
146 #library('$library');
147 #import('dart:math');
148 #import('dart:isolate');
149 #import('$unittest', prefix:'unittest');
150 #import('$sourceName', prefix: 'test');
151 """;
152 }
153
154 // The core skeleton for a config. Most of the guts is in the
155 // parameter [body].
156 String configuration([String body = '']) {
157 return """
158 class TestRunnerConfiguration extends unittest.Configuration {
159 get name => 'Test runner configuration';
160 get autoStart => false;
161 $body
162 }
163 """;
164 }
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 }
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(); 144 return sbuf.toString();
409 } 145 }
410 146
411 // Code to support running single tests in isolates. 147 String layoutTestControllerWrapper(String sourceName, String childName,
412 final String runIsolateTestsFunction = """ 148 String expectedDirectory,
413 class TestRunnerChildConfiguration extends unittest.Configuration { 149 String libDirectory) {
414 get name => 'Test runner child configuration'; 150 StringBuffer sbuf = new StringBuffer();
415 get autoStart => false; 151 var htmlFile = childName.replaceFirst('-child.dart', '.html');
152 // Add common prefix.
153 sbuf.add("""
154 #library('layout_controller');
155 #import('dart:uri');
156 #import('dart:io');
157 #import('dart:math');
158 #source('$libDirectory/layout_test_controller.dart');
416 159
417 void onDone(int passed, int failed, int errors, 160 main() {
418 List<unittest.TestCase> results, String uncaughtError) { 161 includeTime = ${config.includeTime};
419 unittest.TestCase test = results[0]; 162 passFormat = '${config.passFormat}';
420 parentPort.send([test.result, test.runningTime.inMilliseconds, 163 failFormat = '${config.failFormat}';
421 test.message, test.stackTrace]); 164 errorFormat = '${config.errorFormat}';
165 listFormat = '${config.listFormat}';
166 drt = '${makePathAbsolute(config.drtPath)}';
167 regenerate = ${config.regenerate};
168 sourceDir = '$expectedDirectory';
169 testfile = '$sourceName';
170 summarize = ${config.produceSummary};
171 baseUrl = 'file://$htmlFile';
172 run${config.layoutText?'Text':'Pixel'}LayoutTest(0);
173 }
174 """);
175 return sbuf.toString();
Siggi Cherem (dart-lang) 2012/09/20 17:37:27 ditto
gram 2012/09/20 18:58:03 Done.
422 } 176 }
423 } 177 }
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