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

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

Issue 10897016: Testrunner for 3rd parties. (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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 // The DartWrapTask generates a Dart wrapper for a test file, that has a
6 // test Configuration customized for the options specified by the user.
7 class DartWrapTask extends PipelineTask {
8 String sourceFileTemplate;
Adam 2012/08/30 06:53:07 Should have a final keyword?
gram 2012/08/30 16:28:50 Done.
9 String tempDartFileTemplate;
10
11 DartWrapTask(this.sourceFileTemplate, this.tempDartFileTemplate);
12
13 void execute(Path testfile, List stdout, List stderr, bool verboseLogging,
14 Function exitHandler) {
15 // Get the source test file and canonicalize the path.
16 var sourceName = makePathAbsolute(expandMacros(sourceFileTemplate, testfile) );
17 // Get the destination file.
18 var destFile = expandMacros(tempDartFileTemplate, testfile);
19
20 // Working buffer for the Dart wrapper.
21 StringBuffer sbuf = new StringBuffer();
22
23 // Add the common header stuff.
24 var p = new Path(sourceName);
25 sbuf.add(directives(p.filenameWithoutExtension,
26 config.unittestPath,
27 sourceName));
28
29 // Add the test configuration and determine the action function.
30 var action;
31 if (config.listTests) {
32 action = 'listTests';
33 sbuf.add(barebonesConfig());
34 sbuf.add(listTestsFunction);
35 sbuf.add(formatListMessageFunction(config.listFormat));
36 } else if (config.listGroups) {
37 sbuf.add(barebonesConfig());
38 sbuf.add(listGroupsFunction);
39 sbuf.add(formatListMessageFunction(config.listFormat));
40 action = 'listGroups';
41 } else {
42
43 if (config.runInBrowser) {
44 sbuf.add(browserTestPrintFunction);
45 sbuf.add(unblockDRTFunction);
46 } else {
47 sbuf.add(nonBrowserTestPrintFunction);
48 sbuf.add(stubUnblockDRTFunction);
49 }
50
51 if (config.runIsolated) {
52 sbuf.add(runIsolateTestsFunction);
53 action = 'runIsolateTests';
54 } else {
55 sbuf.add(runTestsFunction);
56 action = 'runTests';
57 }
58
59 sbuf.add(config.includeTime ? elapsedFunction : stubElapsedFunction);
60 sbuf.add(config.produceSummary ?
61 printSummaryFunction : stubPrintSummaryFunction);
62
63 if (config.immediateOutput) {
64 sbuf.add(printResultFunction);
65 sbuf.add(stubPrintResultsFunction);
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 these 2 function names are extremely similar, it t
gram 2012/08/30 17:34:05 Done.
66 } else {
67 sbuf.add(stubPrintResultFunction);
68 sbuf.add(printResultsFunction);
69 }
70
71 sbuf.add(dumpTestResultFunction);
72 sbuf.add(formatMessageFunction(config.passFormat,
73 config.failFormat,
74 config.errorFormat));
75 sbuf.add(testConfig());
76 }
77
78 // Add the filter, if applicable.
79 if (config.filtering) {
80 if (config.includeFilter.length > 0) {
81 sbuf.add(filterTestFunction(config.includeFilter, 'true'));
82 } else {
83 sbuf.add(filterTestFunction(config.excludeFilter, 'false'));
84 }
85 }
86
87 // Add the common trailer stuff.
88 sbuf.add(dartMain(sourceName, action, config.filtering));
89
90 // Save the Dart file.
91 createFile(destFile, sbuf.toString());
92 exitHandler(0);
93 }
94
95 void cleanup(Path testfile, List stdout, List stderr,
96 bool verboseLogging, bool keepTestFiles) {
97 deleteFiles([ tempDartFileTemplate ], testfile,
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 ditto: - space removal: [ ___ ] => [___] - shorter
gram 2012/08/30 17:34:05 Done.
98 verboseLogging, keepTestFiles, stdout);
99 }
100
101 String directives(String library, String unittest, String sourceName) {
102 return """
103 #library('$library');
104 #import('dart:math');
105 #import('dart:isolate');
106 #import('$unittest', prefix:'unittest');
107 #import('$sourceName', prefix: 'test');
108 """;
109 }
110
111 // The core skeleton for a config. Most of the guts is in the
112 // parameter [body].
113 String configuration([String body = '']) {
114 return """
115 class TestRunnerConfiguration extends unittest.Configuration {
116 get name => 'Test runner configuration';
117 get autoStart => false;
118 $body
119 }
120 """;
121 }
122
123 // A barebones config, used for listing tests, not running them.
124 String barebonesConfig() {
125 return configuration();
126 }
127
128 // A more complex config, used for running tests.
129 String testConfig() {
130 return configuration("""
131 void onTestResult(TestCase testCase) {
132 printResult('\$testFile ', testCase);
133 }
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 add empty line
gram 2012/08/30 17:34:05 Done.
134 void onDone(int passed, int failed, int errors, List<TestCase> results,
135 String uncaughtError) {
136 var success = (passed > 0 && failed == 0 && errors == 0 &&
137 uncaughtError == null);
138 printResults(testFile, results);
139 printSummary(testFile, passed, failed, errors, uncaughtError);
140 unblockDRT();
141 }
142 """);
143 }
144
145 // The main function, that creates the config, filters the tests if
146 // necessary, then performs the action (list/run/run-isolated).
147 String dartMain(String sourceName, String action, bool filter) {
148 return """
149 var testFile = '$sourceName';
150 main() {
151 unittest.groupSep = '###';
152 unittest.configure(new TestRunnerConfiguration());
153 unittest.group('', test.main);
154 ${filter?'unittest.filterTests(filterTest);':''}
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 spaces around ? and :
gram 2012/08/30 17:34:05 Done.
155 $action();
156 }
157 """;
158 }
159
160 // For 'printing' when we are in the browser, we add text elements
161 // to a DOM element with id 'console'.
162 final String browserTestPrintFunction = """
163 #import('dart:html');
164 void tprint(msg) {
165 var pre = query('#console');
166 pre.elements.add(new Text('###\$msg\\n'));
167 }
168 """;
169
170 // For printing when not in the browser we can just use Dart's print().
171 final String nonBrowserTestPrintFunction = """
172 void tprint(msg) {
173 print('###\$msg');
174 }
175 """;
176
177 // A function to give us the elapsed time for a test.
178 final String elapsedFunction = """
179 String elapsed(TestCase t) {
180 double duration = t.runningTime.inMilliseconds;
181 duration /= 1000;
182 return '\${duration.toStringAsFixed(3)}s ';
183 }
184 """;
185
186 // A dummy version of the elapsed function for when the user
187 // doesn't want test times included.
188 final String stubElapsedFunction = """
189 String elapsed(TestCase t) {
190 return '';
191 }
192 """;
193
194 // A function to print the results of a test.
195 final String dumpTestResultFunction = """
196 void dumpTestResult(source, TestCase t) {
197 var groupName = '', testName = '';
198 var idx = t.description.lastIndexOf('###');
199 if (idx >= 0) {
200 groupName = t.description.substring(0, idx).replaceAll('###', ' ');
201 testName = t.description.substring(idx+3);
202 } else {
203 testName = t.description;
204 }
205 var stack = (t.stackTrace == null) ? '' : '\${t.stackTrace} ';
206 var message = (t.message.length > 0) ? '\$t.message ' : '';
207 var duration = elapsed(t);
208 tprint(formatMessage(source, '\$groupName ', '\$testName ',
209 duration, t.result, message, stack));
210 }
211 """;
212
213 // A function to print the test summary.
214 final String printSummaryFunction = """
215 void printSummary(String testFile, int passed, int failed, int errors,
216 String uncaughtError) {
217 tprint('');
218 if (passed == 0 && failed == 0 && errors == 0) {
219 tprint('\$testFile: No tests found.');
220 } else if (failed == 0 && errors == 0 && uncaughtError == null) {
221 tprint('\$testFile: All \$passed tests passed.');
222 } else {
223 if (uncaughtError != null) {
224 tprint('\$testFile: Top-level uncaught error: \$uncaughtError');
225 }
226 tprint('\$testFile: \$passed PASSED, \$failed FAILED, \$errors ERRORS');
227 }
228 }
229 """;
230
231 final String stubPrintSummaryFunction = """
232 void printSummary(String testFile, int passed, int failed, int errors,
233 String uncaughtError) {
234 }
235 """;
236
237 // A function to print all test results.
238 final String printResultsFunction = """
239 void printResults(testfile, List<TestCase> results) {
240 for (final testCase in results) {
241 dumpTestResult('\$testfile ', testCase);
242 }
243 }
244 """;
245
246 final String stubPrintResultsFunction = """
247 void printResults(testfile, List<TestCase> results) {
248 }
249 """;
250
251 // A function to print a single test result.
252 final String printResultFunction = """
253 void printResult(testfile, TestCase testCase) {
254 dumpTestResult('\$testfile ', testCase);
255 }
256 """;
257
258 final String stubPrintResultFunction = """
259 void printResult(testfile, TestCase testCase) {
260 }
261 """;
262
263 final String unblockDRTFunction = """
264 void unblockDRT() {
265 window.postMessage('done', '*');
266 }
267 """;
268
269 final String stubUnblockDRTFunction = """
270 void unblockDRT() {
271 }
272 """;
273
274 // A simple format function for listing tests.
275 String formatListMessageFunction(String format) {
276 return """
277 String formatMessage(filename, groupname, [ testname = '']) {
278 return '${format}'.
279 replaceAll('${Macros.testfile}', filename).
280 replaceAll('${Macros.testGroup}', groupname).
281 replaceAll('${Macros.testDescription}', testname);
282 }
283 """;
284 }
285
286 // A richer format function for test results.
287 String formatMessageFunction(
288 String passFormat, String failFormat, String errorFormat) {
289 return """
290 String formatMessage(filename, groupname,
291 [ testname = '', testTime = '', result = '',
292 message = '', stack = '' ]) {
293 var format = '$errorFormat';
294 if (result == 'pass') format = '$passFormat';
295 else if (result == 'fail') format = '$failFormat';
296 return format.
297 replaceAll('${Macros.testTime}', testTime).
298 replaceAll('${Macros.testfile}', filename).
299 replaceAll('${Macros.testGroup}', groupname).
300 replaceAll('${Macros.testDescription}', testname).
301 replaceAll('${Macros.testMessage}', message).
302 replaceAll('${Macros.testStacktrace}', stack);
303 }
304 """;
305 }
306
307 // A function to list the test groups.
308 final String listGroupsFunction = """
309 listGroups() {
310 List tests = unittest.testCases;
311 Map groups = {};
312 for (var t in tests) {
313 var groupName, testName = '';
314 var idx = t.description.lastIndexOf('###');
315 if (idx >= 0) {
316 groupName = t.description.substring(0, idx).replaceAll('###', ' ');
317 if (!groups.containsKey(groupName)) {
318 groups[groupName] = '';
319 }
320 }
321 }
322 for (var g in groups.getKeys()) {
323 var msg = formatMessage('\$testfile ', '\$g ');
324 print('###\$msg');
325 }
326 }
327 """;
328
329 // A function to list the tests.
330 final String listTestsFunction = """
331 listTests() {
332 List tests = unittest.testCases;
333 for (var t in tests) {
334 var groupName, testName = '';
335 var idx = t.description.lastIndexOf('###');
336 if (idx >= 0) {
337 groupName = t.description.substring(0, idx).replaceAll('###', ' ');
338 testName = t.description.substring(idx+3);
339 } else {
340 groupName = '';
341 testName = t.description;
342 }
343 var msg = formatMessage('\$testfile ', '\$groupName ', '\$testName ');
344 print('###\$msg');
345 }
346 }
347 """;
348
349 // A function to filter the tests.
350 String filterTestFunction(List filters, String filterReturnValue) {
351 StringBuffer sbuf = new StringBuffer();
352 sbuf.add('filterTest(t) {\n');
353 if (filters != null) {
354 sbuf.add(' var name = t.description.replaceAll("###", " ");\n');
355 for (var f in filters) {
356 sbuf.add(' if (name.indexOf("$f")>=0) return $filterReturnValue;\n');
357 }
358 sbuf.add(' return !$filterReturnValue;\n');
359 } else {
360 sbuf.add(' return true;\n');
361 }
362 sbuf.add('}\n');
363 return sbuf.toString();
364 }
365
366 // Code to support running single tests as master/slave in isolates.
367 final String runIsolateTestsFunction = """
368 class TestRunnerSlaveConfiguration extends unittest.Configuration {
369 get name => 'Test runner slave configuration';
370 get autoStart => false;
371
372 void onDone(int passed, int failed, int errors, List<TestCase> results,
373 String uncaughtError) {
374 slaveport.send('\${results[0].result}\\n'
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 no need to do this sophisticated encoding in a str
gram 2012/08/30 17:34:05 Done.
375 '\${results[0].runningTime.inMilliseconds}\\n'
376 '\${results[0].message}\\n###\\n\${results[0].stackTrace}');
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 why only results[0], shouldn't onDone send every t
gram 2012/08/30 17:34:05 No - because we run each test in its own isolate,
377 }
378 }
379
380 var slaveport;
381 runSlaveTest() {
382 port.receive((testName, sendport) {
383 slaveport = sendport;
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 slaveport => masterPort? (if this is the 'slave',
gram 2012/08/30 17:34:05 Done.
384 unittest.configure(new TestRunnerSlaveConfiguration());
385 unittest.groupSep = '###';
386 unittest.group('', test.main);
387 unittest.filterTests(testName);
388 unittest.runTests();
389 });
390 }
391
392 var testNum;
393 var failed;
394 var errors;
395 var passed;
396
397 runMasterTest() {
398 var tests = unittest.testCases;
399 tests[testNum].startTime = new Date.now();
400 SendPort sport = spawnFunction(runSlaveTest);
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 sport => slavePort
gram 2012/08/30 17:34:05 Done.
401 sport.call(tests[testNum].description).then((data) {
402 var lines = data.split('\\n');
403 var result = lines[0];
404 var duration = new Duration(milliseconds: parseInt(lines[1]));
405 int idx = 2;
406 StringBuffer buf = new StringBuffer();
407 var sep = '';
408 while (lines[idx] != '###') {
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 see comment above, you shouldn't need to do all th
gram 2012/08/30 17:34:05 Done.
409 buf.add(sep);
410 sep = '\\n';
411 buf.add(lines[idx]);
412 idx++;
413 }
414 var message = buf.toString();
415 ++idx;
416 buf.clear();
417 sep = '';
418 while (idx < lines.length) {
419 buf.add(sep);
420 sep = '\\n';
421 buf.add(lines[idx]);
422 idx++;
423 }
424 var stack = buf.toString();
425 if (result == 'pass') {
426 tests[testNum].pass();
427 ++passed;
428 } else if (result == 'fail') {
429 tests[testNum].fail(message, stack);
430 ++failed;
431 } else {
432 tests[testNum].error(message, stack);
433 ++errors;
434 }
435 tests[testNum].runningTime = duration;
436 ++testNum;
437 if (testNum < tests.length) {
438 runMasterTest();
439 } else {
440 unittest.config.onDone(passed, failed, errors,
441 unittest.testCases, null);
442 }
443 });
444 }
445
446 runIsolateTests() {
447 testNum = 0;
448 passed = failed = errors = 0;
449 runMasterTest();
450 }
451 """;
452
453 // Code for running all tests in the normal (non-isolate) way.
454 final String runTestsFunction = """
455 runTests() {
456 unittest.runTests();
457 }
458 """;
459 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698