OLD | NEW |
---|---|
(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 } | |
OLD | NEW |