blob: 0b1019df8775002135c5a91455fe9f001bb68962 [file] [log] [blame]
// 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.
// TODO(gram): dart2js is not handling 'part of' properly yet; when it does
// uncomment this.
//part of test_controller;
/** Path to DRT executable. */
String drt;
/** Whether to include elapsed time. */
bool includeTime;
/** Whether to regenerate layout test files. */
bool regenerate;
/** Whether to output test summary. */
bool summarize;
/** Whether to print results immediately as they come in. */
bool immediate;
/** Format strings to use for test result messages. */
String listFormat, passFormat, failFormat, errorFormat;
/** Path of the running test file. */
String testfile;
/** The filters must be set by the caller. */
List includeFilters;
List excludeFilters;
/** The print function to use. */
Function tprint;
/** A callback function to notify the caller we are done. */
Function notifyDone;
/** The action function to use. */
Function action;
class Macros {
static const String testTime = '<TIME>';
static const String testfile = '<FILENAME>';
static const String testGroup = '<GROUPNAME>';
static const String testDescription = '<TESTNAME>';
static const String testMessage = '<MESSAGE>';
static const String testStacktrace = '<STACK>';
}
class TestRunnerConfiguration extends unittest.Configuration {
get name => 'Minimal test runner configuration';
get autoStart => false;
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);
}
String elapsed(unittest.TestCase t) {
if (includeTime) {
double duration = t.runningTime.inMilliseconds.toDouble();
duration /= 1000;
return '${duration.toStringAsFixed(3)}s ';
} else {
return '';
}
}
void dumpTestResult(source, unittest.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));
}
void onTestResult(unittest.TestCase testCase) {
if (immediate) {
dumpTestResult('$testfile ', testCase);
}
}
void printSummary(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');
}
}
void onSummary(int passed, int failed, int errors,
List<unittest.TestCase> results, String uncaughtError) {
if (!immediate) {
for (final testCase in results) {
dumpTestResult('$testfile ', testCase);
}
}
if (summarize) {
printSummary(passed, failed, errors, uncaughtError);
}
}
void onDone(bool success) {
if (notifyDone != null) {
notifyDone(success ? 0 : -1);
}
}
}
// Support for listing tests and groups. We use a minimal config.
class MinimalTestRunnerConfiguration extends unittest.Configuration {
get name => 'Minimal test runner configuration';
get autoStart => false;
}
String formatListMessage(filename, groupname, [ testname = '']) {
return listFormat.
replaceAll(Macros.testfile, filename).
replaceAll(Macros.testGroup, groupname).
replaceAll(Macros.testDescription, testname);
}
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.keys) {
var msg = formatListMessage('$testfile ', '$g ');
print('###$msg');
}
if (notifyDone != null) {
notifyDone(0);
}
}
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 = formatListMessage('$testfile ', '$groupName ', '$testName ');
print('###$msg');
}
if (notifyDone != null) {
notifyDone(0);
}
}
// Support for running in isolates.
class TestRunnerChildConfiguration extends unittest.Configuration {
get name => 'Test runner child configuration';
get autoStart => false;
void onSummary(int passed, int failed, int errors,
List<unittest.TestCase> results, String uncaughtError) {
unittest.TestCase test = results[0];
parentPort.send([test.result, test.runningTime.inMilliseconds,
test.message, test.stackTrace]);
}
}
var parentPort;
runChildTest() {
port.receive((testName, sendport) {
parentPort = sendport;
unittest.configure(new TestRunnerChildConfiguration());
unittest.groupSep = '###';
unittest.group('', test.main);
unittest.filterTests(testName);
unittest.runTests();
});
}
var testNum;
var failed;
var errors;
var passed;
runParentTest() {
var tests = unittest.testCases;
tests[testNum].startTime = new DateTime.now();
SendPort childPort = spawnFunction(runChildTest);
childPort.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) {
runParentTest();
} else {
unittest.config.onDone(passed, failed, errors,
unittest.testCases, null);
}
});
}
runIsolateTests() {
testNum = 0;
passed = failed = errors = 0;
runParentTest();
}
// Main
filterTest(t) {
var name = t.description.replaceAll("###", " ");
if (includeFilters.length > 0) {
for (var f in includeFilters) {
if (name.indexOf(f) >= 0) return true;
}
return false;
} else if (excludeFilters.length > 0) {
for (var f in excludeFilters) {
if (name.indexOf(f) >= 0) return false;
}
return true;
} else {
return true;
}
}
process(testMain, action) {
unittest.groupSep = '###';
unittest.configure(new TestRunnerConfiguration());
unittest.group('', testMain);
// Do any user-specified test filtering.
unittest.filterTests(filterTest);
action();
}