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

Unified 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, 4 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 side-by-side diff with in-line comments
Download patch
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,383 @@
+// 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
Siggi Cherem (dart-lang) 2012/08/29 20:47:35 use /** */ so we make all these comments valid dar
Siggi Cherem (dart-lang) 2012/08/30 16:46:40 note that this and the comment below are still pen
gram 2012/08/30 17:34:05 Done.
+// test Configuration customized for the options specified by the user.
+class DartWrapTask extends PipelineTask {
+ String sourceFileTemplate;
+ String tempDartFileTemplate;
+
+ DartWrapTask(this.sourceFileTemplate, this.tempDartFileTemplate);
+
+ void execute(Path testfile, List stdout, List stderr, bool verboseLogging,
+ Function exitHandler) {
+ // Get the source test file and canonicalize the path.
+ var sourceName = makePathAbsolute(expandMacros(sourceFileTemplate, testfile));
Siggi Cherem (dart-lang) 2012/08/29 20:47:35 80 col
gram 2012/08/30 17:34:05 Done.
+ // 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);
+ } else {
+ sbuf.add(nonBrowserTestPrintFunction);
+ }
+
+ if (config.includeTime) {
+ sbuf.add(elapsedFunction);
+ } else {
+ sbuf.add(dummyElapsedFunction);
+ }
+ sbuf.add(dumpTestResultFunction);
+ sbuf.add(testConfig(sourceName,
+ config.runInBrowser,
+ config.runIsolated,
+ config.immediateOutput,
+ config.produceSummary));
+ // Add the isolate stuff, if applicable.
+ if (config.runIsolated) {
+ sbuf.add(runIsolateTestsFunction);
+ action = 'runIsolateTests';
+ } else {
+ sbuf.add(runTestsFunction);
+ action = 'runTests';
+ }
+ sbuf.add(formatMessageFunction(config.passFormat,
+ config.failFormat,
+ config.errorFormat));
+ }
+
+ // 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);
+ }
+
+ String directives(String library, String unittest, String sourceName) {
+ return """
+#library('$library');
+#import('dart:isolate');
+#import('$unittest', prefix:'unittest');
+#import('$sourceName', prefix: 'test');
+""";
+ }
+
+ // 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');
+}
+""";
+
+ // 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 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 dummyElapsedFunction = """
+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 barebones config, used for listing tests, not running them.
+ String barebonesConfig() {
+ return configuration('');
+ }
+
+ // A much more complex config, used for running tests.
+ String testConfig(String sourceName,
+ bool runInBrowser,
+ bool runIsolated,
+ bool immediateOutput,
+ bool summary) {
+ StringBuffer sbuf = new StringBuffer();
+
+ if (runIsolated) {
+ // Add a constructor that saves the isolate port.
+ sbuf.add(" var port;\n TestRunnerConfiguration(this.port);\n");
+ }
+
+ if (immediateOutput) {
+ // Output test results in onTestResult instead of onDone.
+ sbuf.add("""
+ void onTestResult(TestCase testCase) {
+ dumpTestResult('${sourceName} ', testCase);
+ }
+
+ void onDone(int passed, int failed, int errors, List<TestCase> results,
+ String uncaughtError) {
+""");
+ } else {
+ // Not immediate; we output test results in onDone.
+ sbuf.add("""
+ void onDone(int passed, int failed, int errors, List<TestCase> results,
+ String uncaughtError) {
+ // Print each result.
+ for (final testCase in results) {
+ dumpTestResult('${sourceName} ', testCase);
+ }
+""");
+ }
+
+ if (summary) {
+ // Add the code to produce the summary for the test file.
+ sbuf.add("""
+ tprint('');
+ var success = false;
+ if (passed == 0 && failed == 0 && errors == 0) {
+ tprint('$sourceName: No tests found.');
+ } else if (failed == 0 && errors == 0 && uncaughtError == null) {
+ tprint('$sourceName: All \$passed tests passed.');
+ success = true;
+ } else {
+ if (uncaughtError != null) {
+ tprint('$sourceName: Top-level uncaught error: \$uncaughtError');
+ }
+ tprint('$sourceName: \$passed PASSED, \$failed FAILED, \$errors ERRORS');
+ }
+""");
+ }
+
+ if (runIsolated) {
+ // Add the code to tell the master whether we succeeded or not.
+ sbuf.add("""
+ var success = (passed > 0 && failed == 0 && errors == 0 &&
+ uncaughtError == null);
+ port.send(success);
+""");
+ } else if (runInBrowser) {
+ // Add the code to tell DRT we are done and it can exit.
+ sbuf.add(" window.postMessage('done', '*');\n");
+ }
+ sbuf.add(" }\n");
+ return configuration(sbuf.toString());
+ }
+
+ // 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(testfile) {
+ 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(testfile) {
+ 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 {
+ // TODO - is this right? Should it be filterReturnValue?
+ 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 = """
+runSlaveTest() {
+ port.receive((testName, sendport) {
+ unittest.configure(new TestRunnerConfiguration(sendport));
+ unittest.group('', test.main);
+ unittest.filterTests(testName);
+ unittest.runTests();
+ });
+}
+
+var testNum;
+var failed;
+
+runMasterTest() {
+ var tests = unittest.testCases;
+ SendPort sport = spawnFunction(runSlaveTest);
+ sport.call(tests[testNum].description).then((result) {
+ if (!result) failed = true;
+ ++testNum;
+ if (testNum < tests.length) {
+ runMasterTest();
+ } else if (failed) {
+ throw new Exception("Some tests failed.");
+ }
+ });
+}
+
+runIsolateTests(f) {
+ testNum = 0;
+ failed = false;
+ runMasterTest();
+}
+""";
+
+ // Code for running all tests in the normal (non-isolate) way.
+ final String runTestsFunction = "runTests(f) { unittest.runTests(); }\n";
+
+ // 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 """
+main() {
+ unittest.groupSep = '###';
+ unittest.configure(new TestRunnerConfiguration());
+ unittest.group('', test.main);
+ ${filter?'unittest.filterTests(filterTest);':''}
+ $action('$sourceName');
+}
+""";
+ }
+
+}

Powered by Google App Engine
This is Rietveld 408576698