| Index: utils/testrunner/dart_wrap_task.dart
|
| ===================================================================
|
| --- utils/testrunner/dart_wrap_task.dart (revision 0)
|
| +++ utils/testrunner/dart_wrap_task.dart (revision 0)
|
| @@ -0,0 +1,443 @@
|
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +/**
|
| + * The DartWrapTask generates a Dart wrapper for a test file, that has a
|
| + * test Configuration customized for the options specified by the user.
|
| + */
|
| +class DartWrapTask extends PipelineTask {
|
| + final String _sourceFileTemplate;
|
| + final String _tempDartFileTemplate;
|
| +
|
| + DartWrapTask(this._sourceFileTemplate, this._tempDartFileTemplate);
|
| +
|
| + void execute(Path testfile, List stdout, List stderr, bool logging,
|
| + Function exitHandler) {
|
| + // Get the source test file and canonicalize the path.
|
| + var sourceName = makePathAbsolute(
|
| + expandMacros(_sourceFileTemplate, testfile));
|
| + // Get the destination file.
|
| + var destFile = expandMacros(_tempDartFileTemplate, testfile);
|
| +
|
| + // Working buffer for the Dart wrapper.
|
| + StringBuffer sbuf = new StringBuffer();
|
| +
|
| + // Add the common header stuff.
|
| + var p = new Path(sourceName);
|
| + sbuf.add(directives(p.filenameWithoutExtension,
|
| + config.unittestPath,
|
| + sourceName));
|
| +
|
| + // Add the test configuration and determine the action function.
|
| + var action;
|
| + if (config.listTests) {
|
| + action = 'listTests';
|
| + sbuf.add(barebonesConfig());
|
| + sbuf.add(listTestsFunction);
|
| + sbuf.add(formatListMessageFunction(config.listFormat));
|
| + } else if (config.listGroups) {
|
| + sbuf.add(barebonesConfig());
|
| + sbuf.add(listGroupsFunction);
|
| + sbuf.add(formatListMessageFunction(config.listFormat));
|
| + action = 'listGroups';
|
| + } else {
|
| +
|
| + if (config.runInBrowser) {
|
| + sbuf.add(browserTestPrintFunction);
|
| + sbuf.add(unblockDRTFunction);
|
| + } else {
|
| + sbuf.add(nonBrowserTestPrintFunction);
|
| + sbuf.add(stubUnblockDRTFunction);
|
| + }
|
| +
|
| + if (config.runIsolated) {
|
| + sbuf.add(runIsolateTestsFunction);
|
| + action = 'runIsolateTests';
|
| + } else {
|
| + sbuf.add(runTestsFunction);
|
| + action = 'runTests';
|
| + }
|
| +
|
| + sbuf.add(config.includeTime ? elapsedFunction : stubElapsedFunction);
|
| + sbuf.add(config.produceSummary ?
|
| + printSummaryFunction : stubPrintSummaryFunction);
|
| +
|
| + if (config.immediateOutput) {
|
| + sbuf.add(printTestResultFunction);
|
| + sbuf.add(stubPrintAllTestResultsFunction);
|
| + } else {
|
| + sbuf.add(stubPrintTestResultFunction);
|
| + sbuf.add(printAllTestResultsFunction);
|
| + }
|
| +
|
| + sbuf.add(dumpTestResultFunction);
|
| + sbuf.add(formatMessageFunction(config.passFormat,
|
| + config.failFormat,
|
| + config.errorFormat));
|
| + sbuf.add(testConfig());
|
| + }
|
| +
|
| + // Add the filter, if applicable.
|
| + if (config.filtering) {
|
| + if (config.includeFilter.length > 0) {
|
| + sbuf.add(filterTestFunction(config.includeFilter, 'true'));
|
| + } else {
|
| + sbuf.add(filterTestFunction(config.excludeFilter, 'false'));
|
| + }
|
| + }
|
| +
|
| + // Add the common trailer stuff.
|
| + sbuf.add(dartMain(sourceName, action, config.filtering));
|
| +
|
| + // Save the Dart file.
|
| + createFile(destFile, sbuf.toString());
|
| + exitHandler(0);
|
| + }
|
| +
|
| + void cleanup(Path testfile, List stdout, List stderr,
|
| + bool logging, bool keepFiles) {
|
| + deleteFiles([_tempDartFileTemplate], testfile, logging, keepFiles, stdout);
|
| + }
|
| +
|
| + String directives(String library, String unittest, String sourceName) {
|
| + return """
|
| +#library('$library');
|
| +#import('dart:math');
|
| +#import('dart:isolate');
|
| +#import('$unittest', prefix:'unittest');
|
| +#import('$sourceName', prefix: 'test');
|
| +""";
|
| + }
|
| +
|
| + // The core skeleton for a config. Most of the guts is in the
|
| + // parameter [body].
|
| + String configuration([String body = '']) {
|
| + return """
|
| +class TestRunnerConfiguration extends unittest.Configuration {
|
| + get name => 'Test runner configuration';
|
| + get autoStart => false;
|
| + $body
|
| +}
|
| +""";
|
| + }
|
| +
|
| + // A barebones config, used for listing tests, not running them.
|
| + String barebonesConfig() {
|
| + return configuration();
|
| + }
|
| +
|
| + // A more complex config, used for running tests.
|
| + String testConfig() {
|
| + return configuration("""
|
| + void onTestResult(TestCase testCase) {
|
| + printResult('\$testFile ', testCase);
|
| + }
|
| +
|
| + void onDone(int passed, int failed, int errors, List<TestCase> results,
|
| + String uncaughtError) {
|
| + var success = (passed > 0 && failed == 0 && errors == 0 &&
|
| + uncaughtError == null);
|
| + printResults(testFile, results);
|
| + printSummary(testFile, passed, failed, errors, uncaughtError);
|
| + unblockDRT();
|
| + }
|
| +""");
|
| + }
|
| +
|
| + // The main function, that creates the config, filters the tests if
|
| + // necessary, then performs the action (list/run/run-isolated).
|
| + String dartMain(String sourceName, String action, bool filter) {
|
| + return """
|
| +var testFile = '$sourceName';
|
| +main() {
|
| + unittest.groupSep = '###';
|
| + unittest.configure(new TestRunnerConfiguration());
|
| + unittest.group('', test.main);
|
| + ${filter ? 'unittest.filterTests(filterTest);' : ''}
|
| + $action();
|
| +}
|
| +""";
|
| + }
|
| +
|
| + // For 'printing' when we are in the browser, we add text elements
|
| + // to a DOM element with id 'console'.
|
| + final String browserTestPrintFunction = """
|
| +#import('dart:html');
|
| +void tprint(msg) {
|
| +var pre = query('#console');
|
| +pre.elements.add(new Text('###\$msg\\n'));
|
| +}
|
| +""";
|
| +
|
| + // For printing when not in the browser we can just use Dart's print().
|
| + final String nonBrowserTestPrintFunction = """
|
| +void tprint(msg) {
|
| +print('###\$msg');
|
| +}
|
| +""";
|
| +
|
| + // A function to give us the elapsed time for a test.
|
| + final String elapsedFunction = """
|
| +String elapsed(TestCase t) {
|
| + double duration = t.runningTime.inMilliseconds;
|
| + duration /= 1000;
|
| + return '\${duration.toStringAsFixed(3)}s ';
|
| +}
|
| +""";
|
| +
|
| + // A dummy version of the elapsed function for when the user
|
| + // doesn't want test times included.
|
| + final String stubElapsedFunction = """
|
| +String elapsed(TestCase t) {
|
| + return '';
|
| +}
|
| +""";
|
| +
|
| + // A function to print the results of a test.
|
| + final String dumpTestResultFunction = """
|
| +void dumpTestResult(source, TestCase t) {
|
| + var groupName = '', testName = '';
|
| + var idx = t.description.lastIndexOf('###');
|
| + if (idx >= 0) {
|
| + groupName = t.description.substring(0, idx).replaceAll('###', ' ');
|
| + testName = t.description.substring(idx+3);
|
| + } else {
|
| + testName = t.description;
|
| + }
|
| + var stack = (t.stackTrace == null) ? '' : '\${t.stackTrace} ';
|
| + var message = (t.message.length > 0) ? '\$t.message ' : '';
|
| + var duration = elapsed(t);
|
| + tprint(formatMessage(source, '\$groupName ', '\$testName ',
|
| + duration, t.result, message, stack));
|
| +}
|
| +""";
|
| +
|
| + // A function to print the test summary.
|
| + final String printSummaryFunction = """
|
| +void printSummary(String testFile, int passed, int failed, int errors,
|
| + String uncaughtError) {
|
| + tprint('');
|
| + if (passed == 0 && failed == 0 && errors == 0) {
|
| + tprint('\$testFile: No tests found.');
|
| + } else if (failed == 0 && errors == 0 && uncaughtError == null) {
|
| + tprint('\$testFile: All \$passed tests passed.');
|
| + } else {
|
| + if (uncaughtError != null) {
|
| + tprint('\$testFile: Top-level uncaught error: \$uncaughtError');
|
| + }
|
| + tprint('\$testFile: \$passed PASSED, \$failed FAILED, \$errors ERRORS');
|
| + }
|
| +}
|
| +""";
|
| +
|
| + final String stubPrintSummaryFunction = """
|
| +void printSummary(String testFile, int passed, int failed, int errors,
|
| + String uncaughtError) {
|
| +}
|
| +""";
|
| +
|
| + // A function to print all test results.
|
| + final String printAllTestResultsFunction = """
|
| +void printResults(testfile, List<TestCase> results) {
|
| + for (final testCase in results) {
|
| + dumpTestResult('\$testfile ', testCase);
|
| + }
|
| +}
|
| +""";
|
| +
|
| + final String stubPrintAllTestResultsFunction = """
|
| +void printResults(testfile, List<TestCase> results) {
|
| +}
|
| +""";
|
| +
|
| + // A function to print a single test result.
|
| + final String printTestResultFunction = """
|
| +void printResult(testfile, TestCase testCase) {
|
| + dumpTestResult('\$testfile ', testCase);
|
| +}
|
| +""";
|
| +
|
| + final String stubPrintTestResultFunction = """
|
| +void printResult(testfile, TestCase testCase) {
|
| +}
|
| +""";
|
| +
|
| + final String unblockDRTFunction = """
|
| +void unblockDRT() {
|
| + window.postMessage('done', '*');
|
| +}
|
| + """;
|
| +
|
| + final String stubUnblockDRTFunction = """
|
| +void unblockDRT() {
|
| +}
|
| +""";
|
| +
|
| + // A simple format function for listing tests.
|
| + String formatListMessageFunction(String format) {
|
| + return """
|
| +String formatMessage(filename, groupname, [ testname = '']) {
|
| + return '${format}'.
|
| + replaceAll('${Macros.testfile}', filename).
|
| + replaceAll('${Macros.testGroup}', groupname).
|
| + replaceAll('${Macros.testDescription}', testname);
|
| +}
|
| +""";
|
| + }
|
| +
|
| + // A richer format function for test results.
|
| + String formatMessageFunction(
|
| + String passFormat, String failFormat, String errorFormat) {
|
| + return """
|
| +String formatMessage(filename, groupname,
|
| + [ testname = '', testTime = '', result = '',
|
| + message = '', stack = '' ]) {
|
| + var format = '$errorFormat';
|
| + if (result == 'pass') format = '$passFormat';
|
| + else if (result == 'fail') format = '$failFormat';
|
| + return format.
|
| + replaceAll('${Macros.testTime}', testTime).
|
| + replaceAll('${Macros.testfile}', filename).
|
| + replaceAll('${Macros.testGroup}', groupname).
|
| + replaceAll('${Macros.testDescription}', testname).
|
| + replaceAll('${Macros.testMessage}', message).
|
| + replaceAll('${Macros.testStacktrace}', stack);
|
| +}
|
| +""";
|
| + }
|
| +
|
| + // A function to list the test groups.
|
| + final String listGroupsFunction = """
|
| +listGroups() {
|
| + List tests = unittest.testCases;
|
| + Map groups = {};
|
| + for (var t in tests) {
|
| + var groupName, testName = '';
|
| + var idx = t.description.lastIndexOf('###');
|
| + if (idx >= 0) {
|
| + groupName = t.description.substring(0, idx).replaceAll('###', ' ');
|
| + if (!groups.containsKey(groupName)) {
|
| + groups[groupName] = '';
|
| + }
|
| + }
|
| + }
|
| + for (var g in groups.getKeys()) {
|
| + var msg = formatMessage('\$testfile ', '\$g ');
|
| + print('###\$msg');
|
| + }
|
| +}
|
| +""";
|
| +
|
| + // A function to list the tests.
|
| + final String listTestsFunction = """
|
| +listTests() {
|
| + List tests = unittest.testCases;
|
| + for (var t in tests) {
|
| + var groupName, testName = '';
|
| + var idx = t.description.lastIndexOf('###');
|
| + if (idx >= 0) {
|
| + groupName = t.description.substring(0, idx).replaceAll('###', ' ');
|
| + testName = t.description.substring(idx+3);
|
| + } else {
|
| + groupName = '';
|
| + testName = t.description;
|
| + }
|
| + var msg = formatMessage('\$testfile ', '\$groupName ', '\$testName ');
|
| + print('###\$msg');
|
| + }
|
| +}
|
| +""";
|
| +
|
| + // A function to filter the tests.
|
| + String filterTestFunction(List filters, String filterReturnValue) {
|
| + StringBuffer sbuf = new StringBuffer();
|
| + sbuf.add('filterTest(t) {\n');
|
| + if (filters != null) {
|
| + sbuf.add(' var name = t.description.replaceAll("###", " ");\n');
|
| + for (var f in filters) {
|
| + sbuf.add(' if (name.indexOf("$f")>=0) return $filterReturnValue;\n');
|
| + }
|
| + sbuf.add(' return !$filterReturnValue;\n');
|
| + } else {
|
| + sbuf.add(' return true;\n');
|
| + }
|
| + sbuf.add('}\n');
|
| + return sbuf.toString();
|
| + }
|
| +
|
| + // Code to support running single tests as master/slave in isolates.
|
| + final String runIsolateTestsFunction = """
|
| +class TestRunnerSlaveConfiguration extends unittest.Configuration {
|
| + get name => 'Test runner slave configuration';
|
| + get autoStart => false;
|
| +
|
| + void onDone(int passed, int failed, int errors, List<TestCase> results,
|
| + String uncaughtError) {
|
| + TestCase test = results[0];
|
| + masterPort.send([test.result, test.runningTime.inMilliseconds,
|
| + test.message, test.stackTrace]);
|
| + }
|
| +}
|
| +
|
| +var masterPort;
|
| +runSlaveTest() {
|
| + port.receive((testName, sendport) {
|
| + masterPort = sendport;
|
| + unittest.configure(new TestRunnerSlaveConfiguration());
|
| + unittest.groupSep = '###';
|
| + unittest.group('', test.main);
|
| + unittest.filterTests(testName);
|
| + unittest.runTests();
|
| + });
|
| +}
|
| +
|
| +var testNum;
|
| +var failed;
|
| +var errors;
|
| +var passed;
|
| +
|
| +runMasterTest() {
|
| + var tests = unittest.testCases;
|
| + tests[testNum].startTime = new Date.now();
|
| + SendPort slavePort = spawnFunction(runSlaveTest);
|
| + slavePort.call(tests[testNum].description).then((results) {
|
| + var result = results[0];
|
| + var duration = new Duration(milliseconds: results[1]);
|
| + var message = results[2];
|
| + var stack = results[3];
|
| + if (result == 'pass') {
|
| + tests[testNum].pass();
|
| + ++passed;
|
| + } else if (result == 'fail') {
|
| + tests[testNum].fail(message, stack);
|
| + ++failed;
|
| + } else {
|
| + tests[testNum].error(message, stack);
|
| + ++errors;
|
| + }
|
| + tests[testNum].runningTime = duration;
|
| + ++testNum;
|
| + if (testNum < tests.length) {
|
| + runMasterTest();
|
| + } else {
|
| + unittest.config.onDone(passed, failed, errors,
|
| + unittest.testCases, null);
|
| + }
|
| + });
|
| +}
|
| +
|
| +runIsolateTests() {
|
| + testNum = 0;
|
| + passed = failed = errors = 0;
|
| + runMasterTest();
|
| +}
|
| +""";
|
| +
|
| + // Code for running all tests in the normal (non-isolate) way.
|
| + final String runTestsFunction = """
|
| +runTests() {
|
| + unittest.runTests();
|
| +}
|
| +""";
|
| +}
|
|
|