mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 21:20:36 +00:00
Updated testrunner:
- works with latest SDK - understand and works with pub and pubspecs - tested on Linux, Mac and Windows - has its own tests There is still as issue on Windows, where using Process.start to run a .bat file is not terminating until the timeout kills the cmd.exe process. My suspicion is that this is am issue with Process, not testrunner. I have contacted whesse et al about this. Review URL: https://codereview.chromium.org//14247033 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@21969 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
fb6ea9481a
commit
eccaec8f3b
|
@ -21,6 +21,8 @@ ArgParser getOptionParser() {
|
|||
/** A simple HTTP server. Currently handles serving static files. */
|
||||
class HttpTestServer {
|
||||
HttpServer server;
|
||||
List<Function> matchers = [];
|
||||
List<Function> handlers = [];
|
||||
|
||||
/** If set, serve up static files from this directory. */
|
||||
String staticFileDirectory;
|
||||
|
@ -52,21 +54,20 @@ class HttpTestServer {
|
|||
};
|
||||
|
||||
HttpTestServer(int port, this.staticFileDirectory) {
|
||||
server = new HttpServer();
|
||||
try {
|
||||
server.listen("127.0.0.1", port);
|
||||
HttpServer.bind("127.0.0.1", port).then((s) {
|
||||
server = s;
|
||||
print('Server listening on port $port');
|
||||
} catch (e) {
|
||||
print('Server listen on port $port failed');
|
||||
throw e;
|
||||
}
|
||||
server.onError = (e) {
|
||||
};
|
||||
server.defaultRequestHandler =
|
||||
(HttpRequest request, HttpResponse response) {
|
||||
server.listen((HttpRequest request) {
|
||||
for (var i = 0; i < matchers.length; i++) {
|
||||
if (matchers[i](request)) {
|
||||
handlers[i](request);
|
||||
return;
|
||||
}
|
||||
}
|
||||
HttpResponse response = request.response;
|
||||
try {
|
||||
if (staticFileDirectory != null) {
|
||||
String fname = request.path;
|
||||
String fname = request.uri.path;
|
||||
String path = '$staticFileDirectory$fname';
|
||||
File f = new File(path);
|
||||
if (f.existsSync()) {
|
||||
|
@ -78,31 +79,30 @@ class HttpTestServer {
|
|||
new ContentType(ct.substring(0, idx),
|
||||
ct.substring(idx + 1));
|
||||
}
|
||||
f.openInputStream().pipe(response.outputStream);
|
||||
response.addStream(f.openRead()).then((_) => response.close());
|
||||
} else {
|
||||
response.statusCode = HttpStatus.NOT_FOUND;
|
||||
response.reasonPhrase = '$path does not exist';
|
||||
response.outputStream.close();
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
} catch(e,s) {
|
||||
response.statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
response.reasonPhrase = "$e";
|
||||
response.outputStream.writeString(s.toString());
|
||||
response.outputStream.close();
|
||||
response.write(s);
|
||||
response.close();
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void addHandler(Function matcher, handler) {
|
||||
if (handler is Function) {
|
||||
server.addRequestHandler(matcher, handler);
|
||||
} else {
|
||||
server.addRequestHandler(matcher, handler.onRequest);
|
||||
}
|
||||
void addHandler(Function matcher, Function handler) {
|
||||
matchers.add(matcher);
|
||||
handlers.add(handler);
|
||||
}
|
||||
|
||||
void close() {
|
||||
server.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,177 +124,223 @@ complete() {
|
|||
notifyDone(failCount > 0 ? -1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run an external process [cmd] with command line arguments [args].
|
||||
* [timeout] can be used to forcefully terminate the process after
|
||||
* some number of seconds. This is used by runCommand and startProcess.
|
||||
* If [procId] is non-zero (i.e. called from startProcess) then a reference
|
||||
* to the [Process] will be put in a map with key [procId]; in this case
|
||||
* the process can be terminated later by calling [stopProcess] and
|
||||
* passing in the [procId].
|
||||
* [outputMonitor] is an optional function that will be called back with each
|
||||
* line of output from the process.
|
||||
* Returns a [Future] for when the process terminates.
|
||||
*/
|
||||
Future _processHelper(String command, List<String> args,
|
||||
List stdout, List stderr,
|
||||
int timeout, int procId, Function outputMonitor, bool raw) {
|
||||
var timer = null;
|
||||
return Process.start(command, args).then((process) {
|
||||
|
||||
timer = new Timer(new Duration(seconds: timeout), () {
|
||||
timer = null;
|
||||
process.kill();
|
||||
});
|
||||
|
||||
if (raw) {
|
||||
process.stdout.listen((c) { stdout.addAll(c); });
|
||||
} else {
|
||||
_pipeStream(process.stdout, stdout, outputMonitor);
|
||||
}
|
||||
_pipeStream(process.stderr, stderr, outputMonitor);
|
||||
return process.exitCode;
|
||||
}).then((exitCode) {
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
}
|
||||
return exitCode;
|
||||
})
|
||||
.catchError((e) {
|
||||
stderr.add("#Error starting process $command: ${e.error}");
|
||||
});
|
||||
}
|
||||
|
||||
void _pipeStream(Stream stream, List<String> destination,
|
||||
Function outputMonitor) {
|
||||
stream
|
||||
.transform(new StringDecoder())
|
||||
.transform(new LineTransformer())
|
||||
.listen((String line) {
|
||||
if (outputMonitor != null) {
|
||||
outputMonitor(line);
|
||||
}
|
||||
destination.add(line);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an external process [cmd] with command line arguments [args].
|
||||
* [timeout] can be used to forcefully terminate the process after
|
||||
* some number of seconds.
|
||||
* Returns a [Future] for when the process terminates.
|
||||
*/
|
||||
Future runCommand(String command, List<String> args,
|
||||
List stdout, List stderr,
|
||||
{int timeout: 300, Function outputMonitor,
|
||||
bool raw: false}) {
|
||||
return _processHelper(command, args, stdout, stderr,
|
||||
timeout, 0, outputMonitor, raw);
|
||||
}
|
||||
|
||||
String parseLabel(String line) {
|
||||
if (line.startsWith('CONSOLE MESSAGE')) {
|
||||
var idx = line.indexOf('#TEST ');
|
||||
if (idx > 0) {
|
||||
return line.substring(idx + 6);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
runTextLayoutTest(testNum) {
|
||||
var url = '$baseUrl?test=$testNum';
|
||||
var stdout = new List();
|
||||
var stderr = new List();
|
||||
start = new DateTime.now();
|
||||
Process.start(drt, [url]).then((process) {
|
||||
// Drain stderr to not leak resources.
|
||||
process.stderr.onData = process.stderr.read;
|
||||
StringInputStream stdoutStringStream =
|
||||
new StringInputStream(process.stdout);
|
||||
stdoutStringStream.onLine = () {
|
||||
if (stdoutStringStream.closed) return;
|
||||
var line = stdoutStringStream.readLine();
|
||||
while (null != line) {
|
||||
stdout.add(line);
|
||||
line = stdoutStringStream.readLine();
|
||||
}
|
||||
};
|
||||
process.onExit = (exitCode) {
|
||||
process.close();
|
||||
if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) {
|
||||
stdout.removeLast();
|
||||
}
|
||||
var done = false;
|
||||
var i = 0;
|
||||
var label = null;
|
||||
var labelMarker = 'CONSOLE MESSAGE: #TEST ';
|
||||
var contentMarker = 'layer at ';
|
||||
while (i < stdout.length) {
|
||||
if (label == null && stdout[i].startsWith(labelMarker)) {
|
||||
label = stdout[i].substring(labelMarker.length);
|
||||
if (label == 'NONEXISTENT') {
|
||||
complete();
|
||||
}
|
||||
} else if (stdout[i].startsWith(contentMarker)) {
|
||||
if (label == null) {
|
||||
complete();
|
||||
}
|
||||
var expectedFileName =
|
||||
'$sourceDir${Platform.pathSeparator}'
|
||||
'${label.replaceAll("###", "_")
|
||||
.replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt';
|
||||
var expected = new File(expectedFileName);
|
||||
if (regenerate) {
|
||||
var ostream = expected.openOutputStream(FileMode.WRITE);
|
||||
while (i < stdout.length) {
|
||||
ostream.writeString(stdout[i]);
|
||||
ostream.writeString('\n');
|
||||
i++;
|
||||
}
|
||||
ostream.close();
|
||||
pass(start, label);
|
||||
} else if (!expected.existsSync()) {
|
||||
fail(start, label, 'No expectation file');
|
||||
} else {
|
||||
var lines = expected.readAsLinesSync();
|
||||
var actualLength = stdout.length - i;
|
||||
var compareCount = min(lines.length, actualLength);
|
||||
var match = true;
|
||||
for (var j = 0; j < compareCount; j++) {
|
||||
if (lines[j] != stdout[i + j]) {
|
||||
fail(start, label, 'Expectation differs at line ${j + 1}');
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if (lines.length != actualLength) {
|
||||
fail(start, label, 'Expectation file has wrong length');
|
||||
} else {
|
||||
pass(start, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
runCommand(drt, [url], stdout, stderr).then((e) {
|
||||
if (stdout.length > 0 && stdout[stdout.length-1].startsWith('#EOF')) {
|
||||
stdout.removeLast();
|
||||
}
|
||||
var done = false;
|
||||
var i = 0;
|
||||
var label = null;
|
||||
var contentMarker = 'layer at ';
|
||||
while (i < stdout.length) {
|
||||
if (label == null && (label = parseLabel(stdout[i])) != null) {
|
||||
if (label == 'NONEXISTENT') {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
} else if (stdout[i].startsWith(contentMarker)) {
|
||||
if (label == null) {
|
||||
complete();
|
||||
return;
|
||||
}
|
||||
var expectedFileName =
|
||||
'$sourceDir${Platform.pathSeparator}'
|
||||
'${label.replaceAll("###", "_")
|
||||
.replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.txt';
|
||||
var expected = new File(expectedFileName);
|
||||
if (regenerate) {
|
||||
var osink = expected.openWrite();
|
||||
while (i < stdout.length) {
|
||||
osink.write(stdout[i]);
|
||||
osink.write('\n');
|
||||
i++;
|
||||
}
|
||||
osink.close();
|
||||
pass(start, label);
|
||||
} else if (!expected.existsSync()) {
|
||||
fail(start, label, 'No expectation file');
|
||||
} else {
|
||||
var lines = expected.readAsLinesSync();
|
||||
var actualLength = stdout.length - i;
|
||||
var compareCount = min(lines.length, actualLength);
|
||||
var match = true;
|
||||
for (var j = 0; j < compareCount; j++) {
|
||||
if (lines[j] != stdout[i + j]) {
|
||||
fail(start, label, 'Expectation differs at line ${j + 1}');
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
if (lines.length != actualLength) {
|
||||
fail(start, label, 'Expectation file has wrong length');
|
||||
} else {
|
||||
pass(start, label);
|
||||
}
|
||||
}
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
if (label != null) {
|
||||
if (!done) error(start, label, 'Failed to parse output');
|
||||
runTextLayoutTest(testNum + 1);
|
||||
}
|
||||
};
|
||||
i++;
|
||||
}
|
||||
if (label != null) {
|
||||
if (!done) error(start, label, 'Failed to parse output');
|
||||
runTextLayoutTest(testNum + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
runPixelLayoutTest(int testNum) {
|
||||
var url = '$baseUrl?test=$testNum';
|
||||
var stdout = new List();
|
||||
var stderr = new List();
|
||||
start = new DateTime.now();
|
||||
Process.start(drt, ["$url'-p"]).then((process) {
|
||||
// Drain stderr to not leak resources.
|
||||
process.stderr.onData = process.stderr.read;
|
||||
ListInputStream stdoutStream = process.stdout;
|
||||
stdoutStream.onData = () {
|
||||
if (!stdoutStream.closed) {
|
||||
var data = stdoutStream.read();
|
||||
stdout.addAll(data);
|
||||
runCommand(drt, ["$url'-p"], stdout, stderr, raw:true).then((exitCode) {
|
||||
var contentMarker = 'Content-Length: ';
|
||||
var eol = '\n'.codeUnitAt(0);
|
||||
var pos = 0;
|
||||
var label = null;
|
||||
var done = false;
|
||||
|
||||
while(pos < stdout.length) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (pos < stdout.length && stdout[pos] != eol) {
|
||||
sb.writeCharCode(stdout[pos++]);
|
||||
}
|
||||
};
|
||||
stdoutStream.onError = (e) {
|
||||
print(e);
|
||||
};
|
||||
process.onExit = (exitCode) {
|
||||
stdout.addAll(process.stdout.read());
|
||||
process.close();
|
||||
var labelMarker = 'CONSOLE MESSAGE: #TEST ';
|
||||
var contentMarker = 'Content-Length: ';
|
||||
var eol = '\n'.codeUnitAt(0);
|
||||
var pos = -1;
|
||||
var label = null;
|
||||
var done = false;
|
||||
if (++pos >= stdout.length && line == '') break;
|
||||
var line = sb.toString();
|
||||
|
||||
while(pos < stdout.length) {
|
||||
var idx = stdout.indexOf(eol, ++pos);
|
||||
if (idx < 0) break;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (var i = pos; i < idx; i++) {
|
||||
sb.writeCharCode(stdout[i]);
|
||||
if (label == null && (label = parseLabel(line)) != null) {
|
||||
if (label == 'NONEXISTENT') {
|
||||
complete();
|
||||
}
|
||||
var line = sb.toString();
|
||||
|
||||
if (label == null && line.startsWith(labelMarker)) {
|
||||
label = line.substring(labelMarker.length);
|
||||
if (label == 'NONEXISTENT') {
|
||||
complete();
|
||||
}
|
||||
} else if (line.startsWith(contentMarker)) {
|
||||
if (label == null) {
|
||||
complete();
|
||||
}
|
||||
var len = int.parse(line.substring(contentMarker.length));
|
||||
pos = idx + 1;
|
||||
var expectedFileName =
|
||||
'$sourceDir${Platform.pathSeparator}'
|
||||
'${label.replaceAll("###","_").
|
||||
replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png';
|
||||
var expected = new File(expectedFileName);
|
||||
if (regenerate) {
|
||||
var ostream = expected.openOutputStream(FileMode.WRITE);
|
||||
ostream.writeFrom(stdout, pos, len);
|
||||
ostream.close();
|
||||
pass(start, label);
|
||||
} else if (!expected.existsSync()) {
|
||||
fail(start, label, 'No expectation file');
|
||||
} else if (line.startsWith(contentMarker)) {
|
||||
if (label == null) {
|
||||
complete();
|
||||
}
|
||||
var len = int.parse(line.substring(contentMarker.length));
|
||||
var expectedFileName =
|
||||
'$sourceDir${Platform.pathSeparator}'
|
||||
'${label.replaceAll("###","_").
|
||||
replaceAll(new RegExp("[^A-Za-z0-9]"),"_")}.png';
|
||||
var expected = new File(expectedFileName);
|
||||
if (regenerate) {
|
||||
var osink = expected.openWrite();
|
||||
stdout.removeRange(0, pos);
|
||||
stdout.length = len;
|
||||
osink.add(stdout);
|
||||
osink.close();
|
||||
pass(start, label);
|
||||
} else if (!expected.existsSync()) {
|
||||
fail(start, label, 'No expectation file');
|
||||
} else {
|
||||
var bytes = expected.readAsBytesSync();
|
||||
if (bytes.length != len) {
|
||||
fail(start, label, 'Expectation file has wrong length');
|
||||
} else {
|
||||
var bytes = expected.readAsBytesSync();
|
||||
if (bytes.length != len) {
|
||||
fail(start, label, 'Expectation file has wrong length');
|
||||
} else {
|
||||
var match = true;
|
||||
for (var j = 0; j < len; j++) {
|
||||
if (bytes[j] != stdout[pos + j]) {
|
||||
fail(start, label, 'Expectation differs at byte ${j + 1}');
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
var match = true;
|
||||
for (var j = 0; j < len; j++) {
|
||||
if (bytes[j] != stdout[pos + j]) {
|
||||
fail(start, label, 'Expectation differs at byte ${j + 1}');
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
if (match) pass(start, label);
|
||||
}
|
||||
if (match) pass(start, label);
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
pos = idx;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
if (label != null) {
|
||||
if (!done) error(start, label, 'Failed to parse output');
|
||||
runPixelLayoutTest(testNum + 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (label != null) {
|
||||
if (!done) error(start, label, 'Failed to parse output');
|
||||
runPixelLayoutTest(testNum + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,18 @@
|
|||
// 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.
|
||||
|
||||
part of layout_test;
|
||||
|
||||
// The filters must be set by the caller that #sources this file.
|
||||
List includeFilters;
|
||||
|
||||
// TODO(gram): dart2js is not handling 'part of' properly yet; when it
|
||||
// does uncomment this.
|
||||
//part of layout_test;
|
||||
|
||||
List excludeFilters;
|
||||
|
||||
/**
|
||||
* A special marker string used to separate group names and
|
||||
* identify non-debug output.
|
||||
*/
|
||||
final marker = '###';
|
||||
|
||||
class LayoutTestConfiguration extends unittest.Configuration {
|
||||
get autoStart => false;
|
||||
void onTestResult(unittest.TestCase testCase) {
|
||||
|
@ -19,7 +22,7 @@ class LayoutTestConfiguration extends unittest.Configuration {
|
|||
}
|
||||
|
||||
filterTest(t) {
|
||||
var name = t.description.replaceAll("###", " ");
|
||||
var name = t.description.replaceAll(marker, " ");
|
||||
if (includeFilters.length > 0) {
|
||||
for (var f in includeFilters) {
|
||||
if (name.indexOf(f) >= 0) return true;
|
||||
|
@ -36,8 +39,8 @@ filterTest(t) {
|
|||
}
|
||||
|
||||
runTests(testMain) {
|
||||
unittest.groupSep = '###';
|
||||
unittest.configure(new LayoutTestConfiguration());
|
||||
unittest.groupSep = marker;
|
||||
unittest.unittestConfiguration = new LayoutTestConfiguration();
|
||||
|
||||
// Create the set of test cases.
|
||||
unittest.group('', testMain);
|
||||
|
|
|
@ -25,6 +25,9 @@ ArgParser getOptionParser() {
|
|||
parser.addFlag('checked', defaultsTo: false,
|
||||
help: 'Run tests in checked mode.');
|
||||
|
||||
parser.addFlag('sort', defaultsTo: false,
|
||||
help: 'Sort test files before running.');
|
||||
|
||||
parser.addFlag('layout-text', defaultsTo: false,
|
||||
help: 'Run text layout tests.');
|
||||
|
||||
|
@ -78,8 +81,8 @@ ArgParser getOptionParser() {
|
|||
parser.addFlag('list-groups', defaultsTo: false,
|
||||
help: 'List test groups only, do not run tests.');
|
||||
|
||||
parser.addFlag('keep-files', defaultsTo: false,
|
||||
help: 'Keep the generated files in the temporary directory.');
|
||||
parser.addFlag('clean-files', defaultsTo: false,
|
||||
help: 'Remove the generated files from the temporary directory.');
|
||||
|
||||
parser.addFlag('list-options', defaultsTo: false,
|
||||
help: 'Print non-default option settings, usable as a test.config.');
|
||||
|
@ -92,7 +95,7 @@ ArgParser getOptionParser() {
|
|||
defaultsTo: false);
|
||||
|
||||
parser.addFlag('stop-on-failure', defaultsTo: false,
|
||||
help: 'Stop execution upon first failure.');
|
||||
help: 'Stop execution after first file with failures.');
|
||||
|
||||
parser.addFlag('isolate', defaultsTo: false,
|
||||
help: 'Runs each test in a separate isolate.');
|
||||
|
@ -101,18 +104,14 @@ ArgParser getOptionParser() {
|
|||
|
||||
parser.addOption('dartsdk', help: 'Path to dart SDK.');
|
||||
|
||||
// The defaults here should be the name of the executable, with
|
||||
// the assumption that it is available on the PATH.
|
||||
parser.addOption('dart2js', help: 'Path to dart2js executable.',
|
||||
defaultsTo: 'dart2js');
|
||||
parser.addOption('dart', help: 'Path to dart executable.',
|
||||
defaultsTo: 'dart');
|
||||
parser.addOption('drt', help: 'Path to DumpRenderTree executable.',
|
||||
defaultsTo: 'drt');
|
||||
|
||||
var tmp;
|
||||
if (Platform.operatingSystem == 'windows') {
|
||||
tmp = "c:\\tmp\\test";
|
||||
} else {
|
||||
tmp = "/tmp/test";
|
||||
}
|
||||
parser.addOption('tempdir', help: 'Directory to store temp files.',
|
||||
defaultsTo: '${Platform.pathSeparator}tmp'
|
||||
'${Platform.pathSeparator}testrunner');
|
||||
defaultsTo: tmp);
|
||||
|
||||
parser.addOption('test-file-pattern',
|
||||
help: 'A regular expression that test file names must match '
|
||||
|
@ -147,8 +146,6 @@ ArgParser getOptionParser() {
|
|||
parser.addOption('root',
|
||||
help: 'Root directory for HTTP server for static files');
|
||||
|
||||
parser.addOption('unittest', help: '#import path for unit test library.');
|
||||
|
||||
parser.addOption('pipeline',
|
||||
help: 'Pipeline script to use to run each test file.',
|
||||
defaultsTo: 'run_pipeline.dart');
|
||||
|
@ -157,34 +154,34 @@ ArgParser getOptionParser() {
|
|||
}
|
||||
|
||||
/** Print a value option, quoting it if it has embedded spaces. */
|
||||
_printValueOption(String name, value, OutputStream stream) {
|
||||
_printValueOption(String name, value, IOSink dest) {
|
||||
if (value.indexOf(' ') >= 0) {
|
||||
stream.writeString("--$name='$value'\n");
|
||||
dest.write("--$name='$value'\n");
|
||||
} else {
|
||||
stream.writeString("--$name=$value\n");
|
||||
dest.write("--$name=$value\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** Print the current option values. */
|
||||
printOptions(ArgParser parser, ArgResults arguments,
|
||||
bool includeDefaults, OutputStream stream) {
|
||||
if (stream == null) return;
|
||||
bool includeDefaults, IOSink dest) {
|
||||
if (dest == null) return;
|
||||
for (var name in arguments.options) {
|
||||
if (!name.startsWith('list-')) {
|
||||
var value = arguments[name];
|
||||
var defaultValue = parser.getDefault(name);
|
||||
if (value is bool) {
|
||||
if (includeDefaults || (value != defaultValue)) {
|
||||
stream.writeString('--${value ? "" : "no-"}$name\n');
|
||||
dest.write('--${value ? "" : "no-"}$name\n');
|
||||
}
|
||||
} else if (value is List) {
|
||||
if (value.length > 0) {
|
||||
for (var v in value) {
|
||||
_printValueOption(name, v, stream);
|
||||
_printValueOption(name, v, dest);
|
||||
}
|
||||
}
|
||||
} else if (value != null && (includeDefaults || value != defaultValue)) {
|
||||
_printValueOption(name, value, stream);
|
||||
_printValueOption(name, value, dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +203,7 @@ ArgResults loadConfiguration(optionsParser) {
|
|||
// multi-valued they will take precedence over the ones in test.config.
|
||||
var commandLineArgs = new Options().arguments;
|
||||
var cfgarg = '--configfile';
|
||||
var cfgarge = '--configfile=';
|
||||
for (var i = 0; i < commandLineArgs.length; i++) {
|
||||
if (commandLineArgs[i].startsWith(cfgarg)) {
|
||||
if (commandLineArgs[i] == cfgarg) {
|
||||
|
@ -214,9 +212,9 @@ ArgResults loadConfiguration(optionsParser) {
|
|||
}
|
||||
options.addAll(getFileContents(commandLineArgs[++i], true).
|
||||
where((e) => e.trim().length > 0 && e[0] != '#'));
|
||||
} else if (commandLineArgs[i].startsWith('$cfgarg=')) {
|
||||
} else if (commandLineArgs[i].startsWith(cfgarge)) {
|
||||
options.addAll(
|
||||
getFileContents(commandLineArgs[i].substring(cfgarg.length), true).
|
||||
getFileContents(commandLineArgs[i].substring(cfgarge.length), true).
|
||||
where((e) => e.trim().length > 0 && e[0] != '#'));
|
||||
} else {
|
||||
throw new Exception('Missing argument to $cfgarg');
|
||||
|
@ -247,10 +245,6 @@ bool isSane(ArgResults config) {
|
|||
print('Missing required option --runtime');
|
||||
return false;
|
||||
}
|
||||
if (config['unittest'] == null) {
|
||||
print('Missing required option --unittest');
|
||||
return false;
|
||||
}
|
||||
if (config['include'].length > 0 &&
|
||||
config['exclude'].length > 0) {
|
||||
print('--include and --exclude are mutually exclusive.');
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
part of pipeline;
|
||||
|
||||
List stdout, stderr, log;
|
||||
List log;
|
||||
var replyPort;
|
||||
int _procId = 1;
|
||||
Map _procs = {};
|
||||
|
@ -14,7 +14,7 @@ Map _procs = {};
|
|||
* [tmpDir] directory, with name [basis], but with any extension
|
||||
* stripped and replaced by [suffix].
|
||||
*/
|
||||
String createTempName(String tmpDir, String basis, String suffix) {
|
||||
String createTempName(String tmpDir, String basis, [String suffix='']) {
|
||||
var p = new Path(basis);
|
||||
return '$tmpDir${Platform.pathSeparator}'
|
||||
'${p.filenameWithoutExtension}${suffix}';
|
||||
|
@ -41,9 +41,7 @@ String getDirectory(String file) =>
|
|||
/** Create a file [fileName] and populate it with [contents]. */
|
||||
void writeFile(String fileName, String contents) {
|
||||
var file = new File(fileName);
|
||||
var ostream = file.openOutputStream(FileMode.WRITE);
|
||||
ostream.writeString(contents);
|
||||
ostream.close();
|
||||
file.writeAsStringSync(contents);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -59,62 +57,62 @@ void writeFile(String fileName, String contents) {
|
|||
* Returns a [Future] for when the process terminates.
|
||||
*/
|
||||
Future _processHelper(String command, List<String> args,
|
||||
[int timeout = 300, int procId = 0, Function outputMonitor]) {
|
||||
var completer = procId == 0 ? new Completer() : null;
|
||||
log.add('Running $command ${args.join(" ")}');
|
||||
List stdout, List stderr,
|
||||
[int timeout = 30, int procId = 0, Function outputMonitor]) {
|
||||
var timer = null;
|
||||
var stdoutHandler, stderrHandler;
|
||||
var processFuture = Process.start(command, args);
|
||||
processFuture.then((process) {
|
||||
_procs[procId] = process;
|
||||
if (Platform.operatingSystem == 'windows' && command.endsWith('.bat')) {
|
||||
var oldArgs = args;
|
||||
args = new List();
|
||||
args.add('/c');
|
||||
// TODO(gram): We may need some escaping here if any of the
|
||||
// components contain spaces.
|
||||
args.add("$command ${oldArgs.join(' ')}");
|
||||
command='cmd.exe';
|
||||
}
|
||||
log.add('Running $command ${args.join(" ")}');
|
||||
|
||||
return Process.start(command, args)
|
||||
.then((process) {
|
||||
_procs[procId.toString()] = process;
|
||||
|
||||
timer = new Timer(new Duration(seconds: timeout), () {
|
||||
timer = null;
|
||||
process.kill();
|
||||
});
|
||||
var stdoutFuture = _pipeStream(process.stdout, stdout, outputMonitor);
|
||||
var stderrFuture = _pipeStream(process.stderr, stderr, outputMonitor);
|
||||
|
||||
process.onExit = (exitCode) {
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
}
|
||||
process.close();
|
||||
if (completer != null) {
|
||||
completer.complete(exitCode);
|
||||
}
|
||||
};
|
||||
|
||||
_pipeStream(process.stdout, stdout, outputMonitor);
|
||||
_pipeStream(process.stderr, stderr, outputMonitor);
|
||||
});
|
||||
processFuture.handleException((e) {
|
||||
stderr.add("Error starting process:");
|
||||
stderr.add(" Command: $command");
|
||||
stderr.add(" Error: $e");
|
||||
completePipeline(-1);
|
||||
return true;
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
timer = new Timer(new Duration(seconds: timeout), () {
|
||||
timer = null;
|
||||
process.kill();
|
||||
});
|
||||
return Future.wait([process.exitCode, stdoutFuture, stderrFuture])
|
||||
.then((values) {
|
||||
if (timer != null) {
|
||||
timer.cancel();
|
||||
}
|
||||
return values[0];
|
||||
});
|
||||
})
|
||||
.catchError((e) {
|
||||
stderr.add("Error starting process:");
|
||||
stderr.add(" Command: $command");
|
||||
stderr.add(" Error: ${e.toString()}");
|
||||
return new Future.value(-1);
|
||||
});
|
||||
}
|
||||
|
||||
void _pipeStream(InputStream stream, List<String> destination,
|
||||
Future _pipeStream(Stream stream, List<String> destination,
|
||||
Function outputMonitor) {
|
||||
var source = new StringInputStream(stream);
|
||||
source.onLine = () {
|
||||
if (source.available() == 0) return;
|
||||
var line = source.readLine();
|
||||
while (null != line) {
|
||||
return stream
|
||||
.transform(new StringDecoder())
|
||||
.transform(new LineTransformer())
|
||||
.listen((String line) {
|
||||
if (config["immediate"] && line.startsWith('###')) {
|
||||
// TODO - when we dump the list later skip '###' messages if immediate.
|
||||
print(line.substring(3));
|
||||
}
|
||||
if (outputMonitor != null) {
|
||||
outputMonitor(line);
|
||||
}
|
||||
destination.add(line);
|
||||
line = source.readLine();
|
||||
}
|
||||
};
|
||||
})
|
||||
.asFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,26 +122,32 @@ void _pipeStream(InputStream stream, List<String> destination,
|
|||
* Returns a [Future] for when the process terminates.
|
||||
*/
|
||||
Future runCommand(String command, List<String> args,
|
||||
[int timeout = 300, Function outputMonitor]) {
|
||||
return _processHelper(command, args, timeout, outputMonitor:outputMonitor);
|
||||
List stdout, List stderr,
|
||||
[int timeout = 30, Function outputMonitor]) {
|
||||
return _processHelper(command, args, stdout, stderr,
|
||||
timeout, 0, outputMonitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an external process [cmd] with command line arguments [args].
|
||||
* Returns an ID by which it can later be stopped.
|
||||
*/
|
||||
int startProcess(String command, List<String> args, [Function outputMonitor]) {
|
||||
int startProcess(String command, List<String> args, List stdout, List stderr,
|
||||
[Function outputMonitor]) {
|
||||
int id = _procId++;
|
||||
_processHelper(command, args, 3000, id,
|
||||
outputMonitor:outputMonitor).then((e) {
|
||||
_procs.remove(id);
|
||||
});
|
||||
var f = _processHelper(command, args, stdout, stderr, 3000, id,
|
||||
outputMonitor);
|
||||
if (f != null) {
|
||||
f.then((e) {
|
||||
_procs.remove(id.toString());
|
||||
});
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/** Checks if a process is still running. */
|
||||
bool isProcessRunning(int id) {
|
||||
return _procs.containsKey(id);
|
||||
return _procs.containsKey(id.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,15 +155,16 @@ bool isProcessRunning(int id) {
|
|||
* given the id string.
|
||||
*/
|
||||
void stopProcess(int id) {
|
||||
if (_procs.containsKey(id)) {
|
||||
Process p = _procs.remove(id);
|
||||
var sid = id.toString();
|
||||
if (_procs.containsKey(sid)) {
|
||||
Process p = _procs.remove(sid);
|
||||
p.kill();
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete a file named [fname] if it exists. */
|
||||
bool cleanup(String fname) {
|
||||
if (fname != null && !config['keep-files']) {
|
||||
if (fname != null && config['clean-files']) {
|
||||
var f = new File(fname);
|
||||
try {
|
||||
if (f.existsSync()) {
|
||||
|
@ -173,6 +178,22 @@ bool cleanup(String fname) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Delete a directory named [dname] if it exists. */
|
||||
bool cleanupDir(String dname) {
|
||||
if (dname != null && config['clean-files']) {
|
||||
var d = new Directory(dname);
|
||||
try {
|
||||
if (d.existsSync()) {
|
||||
logMessage('Removing $dname');
|
||||
d.deleteSync(recursive: true);
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
initPipeline(port) {
|
||||
replyPort = port;
|
||||
stdout = new List();
|
||||
|
@ -180,9 +201,12 @@ initPipeline(port) {
|
|||
log = new List();
|
||||
}
|
||||
|
||||
void completePipeline([exitCode = 0]) {
|
||||
void completePipeline(List stdout, List stderr, [exitCode = 0]) {
|
||||
replyPort.send([stdout, stderr, log, exitCode]);
|
||||
}
|
||||
|
||||
/** Utility function to log diagnostic messages. */
|
||||
void logMessage(msg) => log.add(msg);
|
||||
|
||||
/** Turn file paths into standard form with forward slashes. */
|
||||
String normalizePath(String p) => (new Path(p)).toString();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
name: testrunner
|
||||
dependencies:
|
||||
args: { sdk: args }
|
||||
|
||||
args: any
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
/** The default pipeline code for running a test file. */
|
||||
library pipeline;
|
||||
import 'dart:isolate';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
part 'pipeline_utils.dart';
|
||||
|
||||
/**
|
||||
|
@ -49,16 +51,26 @@ String serverPath;
|
|||
/** Number of attempts we will make to start the HTTP server. */
|
||||
const int MAX_SERVER_TRIES = 10;
|
||||
|
||||
/** Pipeline output. */
|
||||
List stdout;
|
||||
|
||||
/** Pipeline errors. */
|
||||
List stderr;
|
||||
|
||||
/** Directory where test wrappers are created. */
|
||||
String tmpDir;
|
||||
|
||||
void main() {
|
||||
port.receive((cfg, replyPort) {
|
||||
config = cfg;
|
||||
stdout = new List();
|
||||
stderr = new List();
|
||||
initPipeline(replyPort);
|
||||
startHTTPServerStage();
|
||||
});
|
||||
}
|
||||
|
||||
/** Initial pipeline stage - starts the HTTP server, if appropriate. */
|
||||
|
||||
startHTTPServerStage() {
|
||||
if (config["server"]) {
|
||||
serverPath = config["testfile"];
|
||||
|
@ -86,7 +98,8 @@ startHTTPServerStage() {
|
|||
serverPort = int.parse(config["port"]);
|
||||
// Start the HTTP server.
|
||||
serverId = startProcess(config["dart"],
|
||||
[ serverPath, '--port=$serverPort', '--root=$serverRoot']);
|
||||
[ serverPath, '--port=$serverPort', '--root=$serverRoot'],
|
||||
stdout, stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +113,7 @@ void tryStartHTTPServer(Random r, int remainingAttempts) {
|
|||
'--root=$serverRoot');
|
||||
serverId = startProcess(config["dart"],
|
||||
[ serverPath, '--port=$serverPort', '--root=$serverRoot'],
|
||||
stdout, stderr,
|
||||
(line) {
|
||||
if (line.startsWith('Server listening')) {
|
||||
wrapStage();
|
||||
|
@ -115,15 +129,9 @@ void tryStartHTTPServer(Random r, int remainingAttempts) {
|
|||
|
||||
/** Initial pipeline stage - generates Dart and HTML wrapper files. */
|
||||
wrapStage() {
|
||||
var tmpDir = config["tempdir"];
|
||||
tmpDir = config["targetDir"];
|
||||
var testFile = config["testfile"];
|
||||
|
||||
// Make sure the temp dir exists.
|
||||
var d = new Directory(tmpDir);
|
||||
if (!d.existsSync()) {
|
||||
d.createSync();
|
||||
}
|
||||
|
||||
// Generate names for the generated wrapper files.
|
||||
tempDartFile = createTempName(tmpDir, testFile, '.dart');
|
||||
if (config["runtime"] != 'vm') {
|
||||
|
@ -146,50 +154,52 @@ wrapStage() {
|
|||
|
||||
if (config["layout"]) {
|
||||
directives = '''
|
||||
import 'dart:uri';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
part '${config["runnerDir"]}/layout_test_controller.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:uri';
|
||||
part '${normalizePath('${config["runnerDir"]}/layout_test_controller.dart')}';
|
||||
''';
|
||||
extras = '''
|
||||
sourceDir = '${config["expectedDirectory"]}';
|
||||
baseUrl = 'file://$tempHtmlFile';
|
||||
tprint = (msg) => print('###\$msg');
|
||||
notifyDone = (e) => exit(e);
|
||||
baseUrl = 'file://${normalizePath('$tempHtmlFile')}';
|
||||
tprint = (msg) => print('###\$msg');
|
||||
notifyDone = (e) => exit(e);
|
||||
''';
|
||||
} else if (config["runtime"] == "vm") {
|
||||
directives = '''
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import '${config["unittest"]}' as unittest;
|
||||
import '${config["testfile"]}' as test;
|
||||
part '${config["runnerDir"]}/standard_test_runner.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'package:unittest/unittest.dart';
|
||||
import '${normalizePath('${config["testfile"]}')}' as test;
|
||||
part '${normalizePath('${config["runnerDir"]}/standard_test_runner.dart')}';
|
||||
''';
|
||||
extras = '''
|
||||
includeFilters = ${config["include"]};
|
||||
excludeFilters = ${config["exclude"]};
|
||||
tprint = (msg) => print('###\$msg');
|
||||
notifyDone = (e) {};
|
||||
unittest.testState["port"] = $serverPort;
|
||||
includeFilters = ${config["include"]};
|
||||
excludeFilters = ${config["exclude"]};
|
||||
tprint = (msg) => print('###\$msg');
|
||||
notifyDone = (e) { exit(e); };
|
||||
testState["port"] = $serverPort;
|
||||
''';
|
||||
} else {
|
||||
directives = '''
|
||||
import 'dart:html';
|
||||
import 'dart:isolate';
|
||||
import '${config["unittest"]}' as unittest;
|
||||
import '${config["testfile"]}' as test;
|
||||
part '${config["runnerDir"]}/standard_test_runner.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:html';
|
||||
import 'dart:isolate';
|
||||
import 'package:unittest/unittest.dart';
|
||||
import '${normalizePath('${config["testfile"]}')}' as test;
|
||||
part '${normalizePath('${config["runnerDir"]}/standard_test_runner.dart')}';
|
||||
''';
|
||||
extras = '''
|
||||
includeFilters = ${config["include"]};
|
||||
excludeFilters = ${config["exclude"]};
|
||||
tprint = (msg) => query('#console').addText('###\$msg\\n');
|
||||
notifyDone = (e) => window.postMessage('done', '*');
|
||||
unittest.testState["port"] = $serverPort;
|
||||
includeFilters = ${config["include"]};
|
||||
excludeFilters = ${config["exclude"]};
|
||||
tprint = (msg) => query('#console').appendText('###\$msg\\n');
|
||||
notifyDone = (e) => window.postMessage('done', '*');
|
||||
testState["port"] = $serverPort;
|
||||
''';
|
||||
}
|
||||
|
||||
var action = 'process(test.main, unittest.runTests)';
|
||||
var action = 'process(test.main, runTests)';
|
||||
if (config["layout-text"]) {
|
||||
action = 'runTextLayoutTests()';
|
||||
} else if (config["layout-pixel"]) {
|
||||
|
@ -204,44 +214,44 @@ wrapStage() {
|
|||
|
||||
logMessage('Creating $tempDartFile');
|
||||
writeFile(tempDartFile, '''
|
||||
library test_controller;
|
||||
$directives
|
||||
library test_controller;
|
||||
$directives
|
||||
|
||||
main() {
|
||||
immediate = ${config["immediate"]};
|
||||
includeTime = ${config["time"]};
|
||||
passFormat = '${config["pass-format"]}';
|
||||
failFormat = '${config["fail-format"]}';
|
||||
errorFormat = '${config["error-format"]}';
|
||||
listFormat = '${config["list-format"]}';
|
||||
regenerate = ${config["regenerate"]};
|
||||
summarize = ${config["summary"]};
|
||||
testfile = '$testFile';
|
||||
drt = '${config["drt"]}';
|
||||
$extras
|
||||
$action;
|
||||
}
|
||||
main() {
|
||||
immediate = ${config["immediate"]};
|
||||
includeTime = ${config["time"]};
|
||||
passFormat = '${config["pass-format"]}';
|
||||
failFormat = '${config["fail-format"]}';
|
||||
errorFormat = '${config["error-format"]}';
|
||||
listFormat = '${config["list-format"]}';
|
||||
regenerate = ${config["regenerate"]};
|
||||
summarize = ${config["summary"]};
|
||||
testfile = '${testFile.replaceAll("\\","\\\\")}';
|
||||
drt = '${config["drt"].replaceAll("\\","\\\\")}';
|
||||
$extras
|
||||
$action;
|
||||
}
|
||||
''');
|
||||
|
||||
// Create the child wrapper for layout tests.
|
||||
if (config["layout"]) {
|
||||
logMessage('Creating $tempChildDartFile');
|
||||
writeFile(tempChildDartFile, '''
|
||||
library layout_test;
|
||||
import 'dart:math';
|
||||
import 'dart:isolate';
|
||||
import 'dart:html';
|
||||
import 'dart:uri';
|
||||
import '${config["unittest"]}' as 'unittest' ;
|
||||
import '$testFile', prefix: 'test' ;
|
||||
part '${config["runnerDir"]}/layout_test_runner.dart';
|
||||
library layout_test;
|
||||
import 'dart:math';
|
||||
import 'dart:isolate';
|
||||
import 'dart:html';
|
||||
import 'dart:uri';
|
||||
import 'package:unittest/unittest.dart' as unittest;
|
||||
import '${normalizePath('$testFile')}' as test;
|
||||
part '${normalizePath('${config["runnerDir"]}/layout_test_runner.dart')}';
|
||||
|
||||
main() {
|
||||
includeFilters = ${config["include"]};
|
||||
excludeFilters = ${config["exclude"]};
|
||||
unittest.testState["port"] = $serverPort;
|
||||
runTests(test.main);
|
||||
}
|
||||
main() {
|
||||
includeFilters = ${config["include"]};
|
||||
excludeFilters = ${config["exclude"]};
|
||||
unittest.testState["port"] = $serverPort;
|
||||
runTests(test.main);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
|
@ -258,7 +268,7 @@ wrapStage() {
|
|||
sourceFile = tempDartFile;
|
||||
scriptFile = isJavascript ? tempJsFile : tempDartFile;
|
||||
bodyElements = '<div id="container"></div><pre id="console"></pre>';
|
||||
runAsText = "window.testRunner.dumpAsText();";
|
||||
runAsText = "testRunner.dumpAsText();";
|
||||
}
|
||||
scriptType = isJavascript ? 'text/javascript' : 'application/dart';
|
||||
|
||||
|
@ -272,13 +282,14 @@ wrapStage() {
|
|||
<title>$testFile</title>
|
||||
<link rel="stylesheet" href="${config["runnerDir"]}/testrunner.css">
|
||||
<script type='text/javascript'>
|
||||
if (window.testRunner) {
|
||||
var testRunner = window.testRunner || window.layoutTestController;
|
||||
if (testRunner) {
|
||||
function handleMessage(m) {
|
||||
if (m.data == 'done') {
|
||||
window.testRunner.notifyDone();
|
||||
testRunner.notifyDone();
|
||||
}
|
||||
}
|
||||
window.testRunner.waitUntilDone();
|
||||
testRunner.waitUntilDone();
|
||||
$runAsText
|
||||
window.addEventListener("message", handleMessage, false);
|
||||
}
|
||||
|
@ -291,9 +302,6 @@ wrapStage() {
|
|||
$bodyElements
|
||||
<script type='$scriptType' src='$scriptFile'></script>
|
||||
</script>
|
||||
<script
|
||||
src="http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js">
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
''');
|
||||
|
@ -305,14 +313,15 @@ src="http://dart.googlecode.com/svn/branches/bleeding_edge/dart/client/dart.js">
|
|||
/** Second stage of pipeline - compiles Dart to Javascript if needed. */
|
||||
compileStage(isJavascript) {
|
||||
if (isJavascript) { // Compile the Dart file.
|
||||
var cmd = config["dart2js"];
|
||||
var input = sourceFile.replaceAll('/', Platform.pathSeparator);
|
||||
var output = scriptFile.replaceAll('/', Platform.pathSeparator);
|
||||
if (config["checked"]) {
|
||||
runCommand(config["dart2js"],
|
||||
[ '--enable_checked_mode', '--out=$scriptFile', '$sourceFile' ]).
|
||||
then(runTestStage);
|
||||
runCommand(cmd, [ '-c', '-o$output', '$input' ], stdout, stderr)
|
||||
.then(runTestStage);
|
||||
} else {
|
||||
runCommand(config["dart2js"],
|
||||
[ '--out=$scriptFile', '$sourceFile' ]).
|
||||
then(runTestStage);
|
||||
runCommand(cmd, [ '-o$output', '$input' ], stdout, stderr)
|
||||
.then(runTestStage);
|
||||
}
|
||||
} else {
|
||||
runTestStage(0);
|
||||
|
@ -334,7 +343,7 @@ runTestStage(_) {
|
|||
cmd = config["drt"];
|
||||
args = [ '--no-timeout', tempHtmlFile ];
|
||||
}
|
||||
runCommand(cmd, args, config["timeout"]).then(cleanupStage);
|
||||
runCommand(cmd, args, stdout, stderr, config["timeout"]).then(cleanupStage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -346,12 +355,15 @@ cleanupStage(exitcode) {
|
|||
stopProcess(serverId);
|
||||
}
|
||||
|
||||
if (!config["keep-files"]) { // Remove the temporary files.
|
||||
if (config["clean-files"]) { // Remove the temporary files.
|
||||
cleanup(tempDartFile);
|
||||
cleanup(tempHtmlFile);
|
||||
cleanup(tempJsFile);
|
||||
cleanup(tempChildDartFile);
|
||||
cleanup(tempChildJsFile);
|
||||
cleanup(createTempName(tmpDir, "pubspec", "yaml"));
|
||||
cleanup(createTempName(tmpDir, "pubspec", "lock"));
|
||||
cleanupDir(createTempName(tmpDir, "packages"));
|
||||
}
|
||||
completePipeline(exitcode);
|
||||
completePipeline(stdout, stderr, exitcode);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
// 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;
|
||||
part of test_controller;
|
||||
|
||||
/** Path to DRT executable. */
|
||||
String drt;
|
||||
|
@ -40,6 +38,12 @@ Function notifyDone;
|
|||
/** The action function to use. */
|
||||
Function action;
|
||||
|
||||
/**
|
||||
* A special marker string used to separate group names and
|
||||
* identify non-debug output.
|
||||
*/
|
||||
final marker = '###';
|
||||
|
||||
class Macros {
|
||||
static const String testTime = '<TIME>';
|
||||
static const String testfile = '<FILENAME>';
|
||||
|
@ -49,10 +53,12 @@ class Macros {
|
|||
static const String testStacktrace = '<STACK>';
|
||||
}
|
||||
|
||||
class TestRunnerConfiguration extends unittest.Configuration {
|
||||
class TestRunnerConfiguration extends Configuration {
|
||||
get name => 'Minimal test runner configuration';
|
||||
get autoStart => false;
|
||||
|
||||
void onInit() {}
|
||||
|
||||
String formatMessage(filename, groupname,
|
||||
[ testname = '', testTime = '', result = '',
|
||||
message = '', stack = '' ]) {
|
||||
|
@ -68,7 +74,7 @@ class TestRunnerConfiguration extends unittest.Configuration {
|
|||
replaceAll(Macros.testStacktrace, stack);
|
||||
}
|
||||
|
||||
String elapsed(unittest.TestCase t) {
|
||||
String elapsed(TestCase t) {
|
||||
if (includeTime) {
|
||||
double duration = t.runningTime.inMilliseconds.toDouble();
|
||||
duration /= 1000;
|
||||
|
@ -78,11 +84,11 @@ class TestRunnerConfiguration extends unittest.Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
void dumpTestResult(source, unittest.TestCase t) {
|
||||
void dumpTestResult(source, TestCase t) {
|
||||
var groupName = '', testName = '';
|
||||
var idx = t.description.lastIndexOf('###');
|
||||
var idx = t.description.lastIndexOf(marker);
|
||||
if (idx >= 0) {
|
||||
groupName = t.description.substring(0, idx).replaceAll('###', ' ');
|
||||
groupName = t.description.substring(0, idx).replaceAll(marker, ' ');
|
||||
testName = t.description.substring(idx+3);
|
||||
} else {
|
||||
testName = t.description;
|
||||
|
@ -94,7 +100,7 @@ class TestRunnerConfiguration extends unittest.Configuration {
|
|||
duration, t.result, message, stack));
|
||||
}
|
||||
|
||||
void onTestResult(unittest.TestCase testCase) {
|
||||
void onTestResult(TestCase testCase) {
|
||||
if (immediate) {
|
||||
dumpTestResult('$testfile ', testCase);
|
||||
}
|
||||
|
@ -116,7 +122,7 @@ class TestRunnerConfiguration extends unittest.Configuration {
|
|||
}
|
||||
|
||||
void onSummary(int passed, int failed, int errors,
|
||||
List<unittest.TestCase> results, String uncaughtError) {
|
||||
List<TestCase> results, String uncaughtError) {
|
||||
if (!immediate) {
|
||||
for (final testCase in results) {
|
||||
dumpTestResult('$testfile ', testCase);
|
||||
|
@ -134,12 +140,6 @@ class TestRunnerConfiguration extends unittest.Configuration {
|
|||
}
|
||||
}
|
||||
|
||||
// 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).
|
||||
|
@ -148,13 +148,13 @@ String formatListMessage(filename, groupname, [ testname = '']) {
|
|||
}
|
||||
|
||||
listGroups() {
|
||||
List tests = unittest.testCases;
|
||||
List tests = testCases;
|
||||
Map groups = {};
|
||||
for (var t in tests) {
|
||||
var groupName, testName = '';
|
||||
var idx = t.description.lastIndexOf('###');
|
||||
var idx = t.description.lastIndexOf(marker);
|
||||
if (idx >= 0) {
|
||||
groupName = t.description.substring(0, idx).replaceAll('###', ' ');
|
||||
groupName = t.description.substring(0, idx).replaceAll(marker, ' ');
|
||||
if (!groups.containsKey(groupName)) {
|
||||
groups[groupName] = '';
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ listGroups() {
|
|||
}
|
||||
for (var g in groups.keys) {
|
||||
var msg = formatListMessage('$testfile ', '$g ');
|
||||
print('###$msg');
|
||||
print('$marker$msg');
|
||||
}
|
||||
if (notifyDone != null) {
|
||||
notifyDone(0);
|
||||
|
@ -170,19 +170,19 @@ listGroups() {
|
|||
}
|
||||
|
||||
listTests() {
|
||||
List tests = unittest.testCases;
|
||||
List tests = testCases;
|
||||
for (var t in tests) {
|
||||
var groupName, testName = '';
|
||||
var idx = t.description.lastIndexOf('###');
|
||||
var idx = t.description.lastIndexOf(marker);
|
||||
if (idx >= 0) {
|
||||
groupName = t.description.substring(0, idx).replaceAll('###', ' ');
|
||||
groupName = t.description.substring(0, idx).replaceAll(marker, ' ');
|
||||
testName = t.description.substring(idx+3);
|
||||
} else {
|
||||
groupName = '';
|
||||
testName = t.description;
|
||||
}
|
||||
var msg = formatListMessage('$testfile ', '$groupName ', '$testName ');
|
||||
print('###$msg');
|
||||
print('$marker$msg');
|
||||
}
|
||||
if (notifyDone != null) {
|
||||
notifyDone(0);
|
||||
|
@ -191,13 +191,13 @@ listTests() {
|
|||
|
||||
// Support for running in isolates.
|
||||
|
||||
class TestRunnerChildConfiguration extends unittest.Configuration {
|
||||
class TestRunnerChildConfiguration extends 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];
|
||||
List<TestCase> results, String uncaughtError) {
|
||||
TestCase test = results[0];
|
||||
parentPort.send([test.result, test.runningTime.inMilliseconds,
|
||||
test.message, test.stackTrace]);
|
||||
}
|
||||
|
@ -207,59 +207,43 @@ var parentPort;
|
|||
runChildTest() {
|
||||
port.receive((testName, sendport) {
|
||||
parentPort = sendport;
|
||||
unittest.configure(new TestRunnerChildConfiguration());
|
||||
unittest.groupSep = '###';
|
||||
unittest.group('', test.main);
|
||||
unittest.filterTests(testName);
|
||||
unittest.runTests();
|
||||
unittestConfiguration = new TestRunnerChildConfiguration();
|
||||
groupSep = marker;
|
||||
group('', test.main);
|
||||
filterTests(testName);
|
||||
runTests();
|
||||
});
|
||||
}
|
||||
|
||||
var testNum;
|
||||
var failed;
|
||||
var errors;
|
||||
var passed;
|
||||
|
||||
runParentTest() {
|
||||
var tests = unittest.testCases;
|
||||
tests[testNum].startTime = new DateTime.now();
|
||||
isolatedTestParentWrapper(testCase) => () {
|
||||
SendPort childPort = spawnFunction(runChildTest);
|
||||
childPort.call(tests[testNum].description).then((results) {
|
||||
var f = childPort.call(testCase.description);
|
||||
f.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);
|
||||
if (result == 'fail') {
|
||||
testCase.fail(message, stack);
|
||||
} else if (result == 'error') {
|
||||
testCase.error(message, stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
return f;
|
||||
};
|
||||
|
||||
runIsolateTests() {
|
||||
testNum = 0;
|
||||
passed = failed = errors = 0;
|
||||
runParentTest();
|
||||
// Replace each test with a wrapped version first.
|
||||
for (var i = 0; i < testCases.length; i++) {
|
||||
testCases[i].testFunction = isolatedTestParentWrapper(testCases[i]);
|
||||
}
|
||||
runTests();
|
||||
}
|
||||
|
||||
// Main
|
||||
|
||||
filterTest(t) {
|
||||
var name = t.description.replaceAll("###", " ");
|
||||
var name = t.description.replaceAll(marker, " ");
|
||||
if (includeFilters.length > 0) {
|
||||
for (var f in includeFilters) {
|
||||
if (name.indexOf(f) >= 0) return true;
|
||||
|
@ -276,10 +260,10 @@ filterTest(t) {
|
|||
}
|
||||
|
||||
process(testMain, action) {
|
||||
unittest.groupSep = '###';
|
||||
unittest.configure(new TestRunnerConfiguration());
|
||||
unittest.group('', testMain);
|
||||
groupSep = marker;
|
||||
unittestConfiguration = new TestRunnerConfiguration();
|
||||
group('', testMain);
|
||||
// Do any user-specified test filtering.
|
||||
unittest.filterTests(filterTest);
|
||||
filterTests(filterTest);
|
||||
action();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
* of the removed features are:
|
||||
*
|
||||
* - No support for test.status files. The assumption is that tests are
|
||||
* expected to pass.
|
||||
* expected to pass. Status file support will be added in the future.
|
||||
* - A restricted set of runtimes. The assumption here is that the Dart
|
||||
* libraries deal with platform dependencies, and so the primary
|
||||
* SKUs that a user of this app would be concerned with would be
|
||||
|
@ -43,7 +43,7 @@
|
|||
* vm - run native Dart in the VM; i.e. using $DARTSDK/dart-sdk/bin/dart.
|
||||
* drt-dart - run native Dart in DumpRenderTree, the headless version of
|
||||
* Dartium, which is located in $DARTSDK/chromium/DumpRenderTree, if
|
||||
* you intsalled the SDK that is bundled with the editor, or available
|
||||
* you installed the SDK that is bundled with the editor, or available
|
||||
* from http://gsdview.appspot.com/dartium-archive/continuous/
|
||||
* otherwise.
|
||||
*
|
||||
|
@ -67,10 +67,27 @@
|
|||
* which is run in an isolate. The `--pipeline` argument can be used to
|
||||
* specify a different script for running a test file pipeline, allowing
|
||||
* customization of the pipeline.
|
||||
*
|
||||
* Wrapper files are created for tests in the tmp directory, which can be
|
||||
* overridden with --tempdir. These files are not removed after the tests
|
||||
* are complete, primarily to reduce the amount of times pub must be
|
||||
* executed. You can use --clean-files to force file cleanup. The temp
|
||||
* directories will have pubspec.yaml files auto-generated unless the
|
||||
* original test file directories have such files; in that case the existing
|
||||
* files will be copied. Whenever a new pubspec file is copied or
|
||||
* created pub will be run (but not otherwise - so if you want to do
|
||||
* the equivelent of pub update you should use --clean-files and the rerun
|
||||
* the tests).
|
||||
*
|
||||
* TODO(gram): if the user has a pubspec.yaml file, we should inspect the
|
||||
* pubspec.lock file and give useful errors:
|
||||
* - if the lock file doesn't exit, then run pub install
|
||||
* - if it exists and it doesn't have the required packages (unittest or
|
||||
* browser), ask the user to add them and run pub install again.
|
||||
*/
|
||||
|
||||
// TODO - layout tests that use PNGs rather than DRT text render dumps.
|
||||
library testrunner;
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
|
@ -91,18 +108,24 @@ int _numTasks;
|
|||
/** The index of the next pipeline runner to execute. */
|
||||
int _nextTask;
|
||||
|
||||
/** The stream to use for high-value messages, like test results. */
|
||||
OutputStream _outStream;
|
||||
/** The sink to use for high-value messages, like test results. */
|
||||
IOSink _outSink;
|
||||
|
||||
/** The stream to use for low-value messages, like verbose output. */
|
||||
OutputStream _logStream;
|
||||
/** The sink to use for low-value messages, like verbose output. */
|
||||
IOSink _logSink;
|
||||
|
||||
/**
|
||||
* The user can specify output streams on the command line, using 'none',
|
||||
* 'stdout', 'stderr', or a file path; [getStream] will take such a name
|
||||
* and return an appropriate [OutputStream].
|
||||
* The last temp test directory we accessed; we use this to know if we
|
||||
* need to check the pub configuration.
|
||||
*/
|
||||
OutputStream getStream(String name) {
|
||||
String _testDir;
|
||||
|
||||
/**
|
||||
* The user can specify output streams on the command line, using 'none',
|
||||
* 'stdout', 'stderr', or a file path; [getSink] will take such a name
|
||||
* and return an appropriate [IOSink].
|
||||
*/
|
||||
IOSink getSink(String name) {
|
||||
if (name == null || name == 'none') {
|
||||
return null;
|
||||
}
|
||||
|
@ -112,7 +135,8 @@ OutputStream getStream(String name) {
|
|||
if (name == 'stderr') {
|
||||
return stderr;
|
||||
}
|
||||
return new File(name).openOutputStream(FileMode.WRITE);
|
||||
var f = new File(name);
|
||||
return f.openWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,13 +144,13 @@ OutputStream getStream(String name) {
|
|||
* and execute pipelines for the files.
|
||||
*/
|
||||
void processTests(Map config, List testFiles) {
|
||||
_outStream = getStream(config['out']);
|
||||
_logStream = getStream(config['log']);
|
||||
_outSink = getSink(config['out']);
|
||||
_logSink = getSink(config['log']);
|
||||
if (config['list-files']) {
|
||||
if (_outStream != null) {
|
||||
if (_outSink != null) {
|
||||
for (var i = 0; i < testFiles.length; i++) {
|
||||
_outStream.writeString(testFiles[i]);
|
||||
_outStream.writeString('\n');
|
||||
_outSink.write(testFiles[i]);
|
||||
_outSink.write('\n');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -137,6 +161,69 @@ void processTests(Map config, List testFiles) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update a pubspec for the target test directory. We use the
|
||||
* source directory pubspec if available; otherwise we create a minimal one.
|
||||
* We return a Future if we are running pub install, or null otherwise.
|
||||
*/
|
||||
Future doPubConfig(Path sourcePath, String sourceDir,
|
||||
Path targetPath, String targetDir,
|
||||
String pub, String runtime) {
|
||||
// Make sure the target directory exists.
|
||||
var d = new Directory(targetDir);
|
||||
if (!d.existsSync()) {
|
||||
d.createSync(recursive: true);
|
||||
}
|
||||
|
||||
// If the source has no pubspec, but the dest does, leave
|
||||
// things as they are. If neither do, create one in dest.
|
||||
|
||||
var sourcePubSpecName = new Path(sourceDir).append("pubspec.yaml").
|
||||
toNativePath();
|
||||
var targetPubSpecName = new Path(targetDir).append("pubspec.yaml").
|
||||
toNativePath();
|
||||
var sourcePubSpec = new File(sourcePubSpecName);
|
||||
var targetPubSpec = new File(targetPubSpecName);
|
||||
|
||||
if (!sourcePubSpec.existsSync()) {
|
||||
if (targetPubSpec.existsSync()) {
|
||||
return null;
|
||||
} else {
|
||||
// Create one.
|
||||
if (runtime == 'vm') {
|
||||
writeFile(targetPubSpecName,
|
||||
"name: testrunner\ndependencies:\n unittest: any\n");
|
||||
} else {
|
||||
writeFile(targetPubSpecName,
|
||||
"name: testrunner\ndependencies:\n unittest: any\n browser: any\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (targetPubSpec.existsSync()) {
|
||||
// If there is a source one, and it is older than the target,
|
||||
// leave the target as is.
|
||||
if (sourcePubSpec.lastModifiedSync().millisecondsSinceEpoch <
|
||||
targetPubSpec.lastModifiedSync().millisecondsSinceEpoch) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Source exists and is newer than target or there is no target;
|
||||
// copy the source to the target. If there is a pubspec.lock file,
|
||||
// copy that too.
|
||||
var s = sourcePubSpec.readAsStringSync();
|
||||
targetPubSpec.writeAsStringSync(s);
|
||||
var sourcePubLock = new File(sourcePubSpecName.replaceAll(".yaml", ".lock"));
|
||||
if (sourcePubLock.existsSync()) {
|
||||
var targetPubLock =
|
||||
new File(targetPubSpecName.replaceAll(".yaml", ".lock"));
|
||||
s = sourcePubLock.readAsStringSync();
|
||||
targetPubLock.writeAsStringSync(s);
|
||||
}
|
||||
}
|
||||
// A new target pubspec was created so run pub install.
|
||||
return _processHelper(pub, [ 'install' ], workingDir: targetDir);
|
||||
}
|
||||
|
||||
/** Execute as many tasks as possible up to the maxTasks limit. */
|
||||
void spawnTasks(Map config, List testFiles) {
|
||||
var verbose = config['verbose'];
|
||||
|
@ -154,12 +241,12 @@ void spawnTasks(Map config, List testFiles) {
|
|||
List stderr = msg[1];
|
||||
List log = msg[2];
|
||||
int exitCode = msg[3];
|
||||
writelog(stdout, _outStream, _logStream, verbose, skipNonVerbose);
|
||||
writelog(stderr, _outStream, _logStream, true, skipNonVerbose);
|
||||
writelog(log, _outStream, _logStream, verbose, skipNonVerbose);
|
||||
writelog(stdout, _outSink, _logSink, verbose, skipNonVerbose);
|
||||
writelog(stderr, _outSink, _logSink, true, skipNonVerbose);
|
||||
writelog(log, _outSink, _logSink, verbose, skipNonVerbose);
|
||||
port.close();
|
||||
--_numTasks;
|
||||
if (exitCode == 0 || !config['stopOnFailure']) {
|
||||
if (exitCode == 0 || !config['stop-on-failure']) {
|
||||
spawnTasks(config, testFiles);
|
||||
}
|
||||
if (_numTasks == 0) {
|
||||
|
@ -168,74 +255,129 @@ void spawnTasks(Map config, List testFiles) {
|
|||
}
|
||||
});
|
||||
SendPort s = spawnUri(config['pipeline']);
|
||||
s.send(config, port.toSendPort());
|
||||
|
||||
// Get the names of the source and target test files and containing
|
||||
// directories.
|
||||
var testPath = new Path(testfile);
|
||||
var sourcePath = testPath.directoryPath;
|
||||
var sourceDir = sourcePath.toNativePath();
|
||||
|
||||
var targetPath = new Path(config["tempdir"]);
|
||||
var normalizedTarget = testPath.directoryPath.toNativePath()
|
||||
.replaceAll(Platform.pathSeparator, '_')
|
||||
.replaceAll(':', '_');
|
||||
targetPath = targetPath.append("${normalizedTarget}_${config['runtime']}");
|
||||
var targetDir = targetPath.toNativePath();
|
||||
|
||||
config['targetDir'] = targetDir;
|
||||
// If this is a new target dir, we need to redo the pub check.
|
||||
var f = null;
|
||||
if (targetDir != _testDir) {
|
||||
f = doPubConfig(sourcePath, sourceDir, targetPath, targetDir,
|
||||
config['pub'], config['runtime']);
|
||||
_testDir = targetDir;
|
||||
}
|
||||
if (f == null) {
|
||||
s.send(config, port.toSendPort());
|
||||
} else {
|
||||
f.then((_) {
|
||||
s.send(config, port.toSendPort());
|
||||
});
|
||||
break; // Don't do any more until pub is done.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Our tests are configured so that critical messages have a '###' prefix.
|
||||
* [writeLog] takes the output from a pipeline execution and writes it to
|
||||
* our output streams. It will strip the '###' if necessary on critical
|
||||
* [writelog] takes the output from a pipeline execution and writes it to
|
||||
* our output sinks. It will strip the '###' if necessary on critical
|
||||
* messages; other messages will only be written if verbose output was
|
||||
* specified.
|
||||
*/
|
||||
void writelog(List messages, OutputStream out, OutputStream log,
|
||||
void writelog(List messages, IOSink out, IOSink log,
|
||||
bool includeVerbose, bool skipNonVerbose) {
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
var msg = messages[i];
|
||||
if (msg.startsWith('###')) {
|
||||
if (!skipNonVerbose && out != null) {
|
||||
out.writeString(msg.substring(3));
|
||||
out.writeString('\n');
|
||||
out.write(msg.substring(3));
|
||||
out.write('\n');
|
||||
}
|
||||
} else if (msg.startsWith('CONSOLE MESSAGE:')) {
|
||||
if (!skipNonVerbose && out != null) {
|
||||
int idx = msg.indexOf('###');
|
||||
if (idx > 0) {
|
||||
out.writeString(msg.substring(idx + 3));
|
||||
out.writeString('\n');
|
||||
out.write(msg.substring(idx + 3));
|
||||
out.write('\n');
|
||||
}
|
||||
}
|
||||
} else if (includeVerbose && log != null) {
|
||||
log.writeString(msg);
|
||||
log.writeString('\n');
|
||||
log.write(msg);
|
||||
log.write('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sanitizeConfig(Map config, ArgParser parser) {
|
||||
normalizeFilter(List filter) {
|
||||
// We want the filter to be a quoted string or list of quoted
|
||||
// strings.
|
||||
for (var i = 0; i < filter.length; i++) {
|
||||
var f = filter[i];
|
||||
if (f[0] != "'" && f[0] != '"') {
|
||||
filter[i] = "'$f'"; // TODO(gram): Quote embedded quotes.
|
||||
}
|
||||
}
|
||||
return filter;
|
||||
}
|
||||
|
||||
void sanitizeConfig(Map config, ArgParser parser) {
|
||||
config['layout'] = config['layout-text'] || config['layout-pixel'];
|
||||
|
||||
// TODO - check if next three are actually used.
|
||||
config['runInBrowser'] = (config['runtime'] != 'vm');
|
||||
config['verbose'] = (config['log'] != 'none' && !config['list-groups']);
|
||||
config['filtering'] = (config['include'].length > 0 ||
|
||||
config['exclude'].length > 0);
|
||||
|
||||
config['timeout'] = int.parse(config['timeout']);
|
||||
config['tasks'] = int.parse(config['tasks']);
|
||||
|
||||
var dartsdk = config['dartsdk'];
|
||||
var pathSep = Platform.pathSeparator;
|
||||
|
||||
if (dartsdk != null) {
|
||||
if (parser.getDefault('dart2js') == config['dart2js']) {
|
||||
config['dart2js'] =
|
||||
'$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js';
|
||||
}
|
||||
if (parser.getDefault('dart') == config['dart']) {
|
||||
config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart';
|
||||
}
|
||||
if (parser.getDefault('drt') == config['drt']) {
|
||||
config['drt'] = '$dartsdk${pathSep}chromium${pathSep}DumpRenderTree';
|
||||
if (dartsdk == null) {
|
||||
var opt = new Options();
|
||||
var runner = opt.executable;
|
||||
var idx = runner.indexOf('dart-sdk');
|
||||
if (idx < 0) {
|
||||
print("Please use --dartsdk option or run using the dart executable "
|
||||
"from the Dart SDK");
|
||||
exit(0);
|
||||
}
|
||||
dartsdk = runner.substring(0, idx);
|
||||
}
|
||||
if (Platform.operatingSystem == 'macos') {
|
||||
config['dart2js'] =
|
||||
'$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js';
|
||||
config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart';
|
||||
config['pub'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}pub';
|
||||
config['drt'] =
|
||||
'$dartsdk/chromium/DumpRenderTree.app/Contents/MacOS/DumpRenderTree';
|
||||
} else if (Platform.operatingSystem == 'linux') {
|
||||
config['dart2js'] =
|
||||
'$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js';
|
||||
config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart';
|
||||
config['pub'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}pub';
|
||||
config['drt'] = '$dartsdk${pathSep}chromium${pathSep}DumpRenderTree';
|
||||
} else {
|
||||
config['dart2js'] =
|
||||
'$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js.bat';
|
||||
config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart.exe';
|
||||
config['pub'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}pub.bat';
|
||||
config['drt'] = '$dartsdk${pathSep}chromium${pathSep}DumpRenderTree.exe';
|
||||
}
|
||||
|
||||
config['unittest'] = makePathAbsolute(config['unittest']);
|
||||
config['drt'] = makePathAbsolute(config['drt']);
|
||||
config['dart'] = makePathAbsolute(config['dart']);
|
||||
config['dart2js'] = makePathAbsolute(config['dart2js']);
|
||||
for (var prog in [ 'drt', 'dart', 'pub', 'dart2js' ]) {
|
||||
config[prog] = makePathAbsolute(config[prog]);
|
||||
}
|
||||
config['runnerDir'] = runnerDirectory;
|
||||
config['include'] = normalizeFilter(config['include']);
|
||||
config['exclude'] = normalizeFilter(config['exclude']);
|
||||
}
|
||||
|
||||
main() {
|
||||
|
@ -245,7 +387,7 @@ main() {
|
|||
if (options['list-options']) {
|
||||
printOptions(optionsParser, options, false, stdout);
|
||||
} else if (options['list-all-options']) {
|
||||
printOptions(optionsParser, options, true, stdout);
|
||||
printOptions(optionsParser, options, true, stdout);
|
||||
} else {
|
||||
var config = new Map();
|
||||
for (var option in options.options) {
|
||||
|
@ -273,9 +415,10 @@ main() {
|
|||
if (dirs.length == 0) {
|
||||
dirs.add('.'); // Use current working directory as default.
|
||||
}
|
||||
buildFileList(dirs,
|
||||
new RegExp(options['test-file-pattern']), options['recurse'],
|
||||
(f) => processTests(config, f));
|
||||
var f = buildFileList(dirs,
|
||||
new RegExp(config['test-file-pattern']), config['recurse']);
|
||||
if (config['sort']) f.sort();
|
||||
processTests(config, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
|
||||
part of testrunner;
|
||||
|
||||
/** Create a file [fileName] and populate it with [contents]. */
|
||||
void writeFile(String fileName, String contents) {
|
||||
var file = new File(fileName);
|
||||
file.writeAsStringSync(contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the contents of a file [fileName] into a [List] of [String]s.
|
||||
* If the file does not exist and [errorIfNoFile] is true, throw an
|
||||
|
@ -28,32 +34,24 @@ List<String> getFileContents(String filename, bool errorIfNoFile) {
|
|||
String makePathAbsolute(String path) {
|
||||
var p = new Path(path).canonicalize();
|
||||
if (p.isAbsolute) {
|
||||
return p.toString();
|
||||
return p.toNativePath();
|
||||
} else {
|
||||
var cwd = new Path((new Directory.current()).path);
|
||||
return cwd.join(p).toString();
|
||||
return cwd.join(p).toNativePath();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the list of all the files in a set of directories
|
||||
* ([dirs]) whose names match [filePat]. If [recurse] is true
|
||||
* look at subdirectories too. Once they have all been enumerated,
|
||||
* call [onComplete]. An optional [excludePat] can be supplied
|
||||
* look at subdirectories too. An optional [excludePat] can be supplied
|
||||
* and files or directories that match that will be excluded.
|
||||
* [includeSymLinks] controls whether or not to include files that
|
||||
* have symlinks in the traversed tree.
|
||||
*/
|
||||
// TODO(gram): The key thing here is we want to avoid package
|
||||
// directories, which have symlinks. excludePat was added for
|
||||
// that but can't currently be used because the symlinked files
|
||||
// have canonicalized paths. So instead we exploit that fact and
|
||||
// assert that every file must have a prefix that matches the
|
||||
// directory. If this changes then we will need to switch to using
|
||||
// the exclude pattern or some other mechanism.
|
||||
void buildFileList(List dirs, RegExp filePat, bool recurse,
|
||||
Function onComplete,
|
||||
List buildFileList(List dirs, RegExp filePat, bool recurse,
|
||||
[RegExp excludePat, bool includeSymLinks = false]) {
|
||||
var files = new List();
|
||||
var dirCount = 1;
|
||||
for (var i = 0; i < dirs.length; i++) {
|
||||
var path = dirs[i];
|
||||
if (excludePat != null && excludePat.hasMatch(path)) {
|
||||
|
@ -69,33 +67,24 @@ void buildFileList(List dirs, RegExp filePat, bool recurse,
|
|||
path = makePathAbsolute(path);
|
||||
Directory d = new Directory(path);
|
||||
if (d.existsSync()) {
|
||||
++dirCount;
|
||||
d.list(recursive: recurse).listen(
|
||||
(entity) {
|
||||
if (entity is File) {
|
||||
var file = entity.name;
|
||||
if (filePat.hasMatch(file)) {
|
||||
if (excludePat == null || !excludePat.hasMatch(file)) {
|
||||
if (includeSymLinks || file.startsWith(path)) {
|
||||
files.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
var contents = d.listSync(recursive: recurse,
|
||||
followLinks: includeSymLinks);
|
||||
for (var entity in contents) {
|
||||
if (entity is File) {
|
||||
var file = entity.path;
|
||||
if (filePat.hasMatch(file)) {
|
||||
if (excludePat == null || !excludePat.hasMatch(file)) {
|
||||
files.add(file);
|
||||
}
|
||||
},
|
||||
onDone: () {
|
||||
if (complete && --dirCount == 0) {
|
||||
onComplete(files);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // Does not exist.
|
||||
print('$path does not exist.');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (--dirCount == 0) {
|
||||
onComplete(files);
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,3 +97,22 @@ String get runnerDirectory {
|
|||
return libDirectory.substring(0,
|
||||
libDirectory.lastIndexOf(Platform.pathSeparator));
|
||||
}
|
||||
|
||||
/*
|
||||
* Run an external process [cmd] with command line arguments [args].
|
||||
* Returns a [Future] for when the process terminates.
|
||||
*/
|
||||
Future _processHelper(String command, List<String> args,
|
||||
{String workingDir}) {
|
||||
var options = null;
|
||||
if (workingDir != null) {
|
||||
options = new ProcessOptions();
|
||||
options.workingDirectory = workingDir;
|
||||
}
|
||||
return Process.run(command, args, options)
|
||||
.then((result) => result.exitCode)
|
||||
.catchError((e) {
|
||||
print("$command ${args.join(' ')}: ${e.toString()}");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
5
utils/tests/testrunner/browser_tests/pubspec.yaml
Executable file
5
utils/tests/testrunner/browser_tests/pubspec.yaml
Executable file
|
@ -0,0 +1,5 @@
|
|||
name: browser_tests
|
||||
dependencies:
|
||||
html: any
|
||||
unittest: any
|
||||
|
24
utils/tests/testrunner/browser_tests/web/browser_test.dart
Executable file
24
utils/tests/testrunner/browser_tests/web/browser_test.dart
Executable file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
library testrunner_test;
|
||||
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
foo(bool x) => x;
|
||||
|
||||
main() {
|
||||
group('group1', () {
|
||||
test('test1', () {
|
||||
expect(true, isFalse);
|
||||
});
|
||||
});
|
||||
group('group2', () {
|
||||
test('test2', () {
|
||||
foo(3);
|
||||
expect(true, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
12
utils/tests/testrunner/browser_tests/web/browser_test.html
Executable file
12
utils/tests/testrunner/browser_tests/web/browser_test.html
Executable file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testdriver tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/dart" src="browser_test.dart"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
library testrunner_test;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:uri';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
main() {
|
||||
var get = (String what, int code, String text) {
|
||||
var c = new Completer();
|
||||
HttpClient client = new HttpClient();
|
||||
client.getUrl(new Uri.fromString("http://127.0.0.1:3456/$what"))
|
||||
.then((HttpClientRequest request) {
|
||||
// Prepare the request then call close on it to send it.
|
||||
return request.close();
|
||||
})
|
||||
.then((HttpClientResponse response) {
|
||||
// Process the response.
|
||||
expect(response.statusCode, code);
|
||||
var sb = new StringBuffer();
|
||||
response.transform(new StringDecoder())
|
||||
.listen((data) {
|
||||
sb.write(data);
|
||||
}, onDone: () {
|
||||
expect(sb.toString(), text);
|
||||
c.complete();
|
||||
});
|
||||
});
|
||||
return c.future;
|
||||
};
|
||||
test('test1', () {
|
||||
return get('test.txt', 200, "Hello world!\n");
|
||||
});
|
||||
test('test2', () {
|
||||
return get('fail.txt', 404, "");
|
||||
});
|
||||
}
|
||||
|
5
utils/tests/testrunner/layout_tests/pubspec.yaml
Executable file
5
utils/tests/testrunner/layout_tests/pubspec.yaml
Executable file
|
@ -0,0 +1,5 @@
|
|||
name: layout_tests
|
||||
dependencies:
|
||||
html: any
|
||||
unittest: any
|
||||
|
17
utils/tests/testrunner/layout_tests/web/layout_test.dart
Executable file
17
utils/tests/testrunner/layout_tests/web/layout_test.dart
Executable file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
library testrunner_test;
|
||||
|
||||
import 'dart:html';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
main() {
|
||||
test("layout", () {
|
||||
var lbl = new LabelElement();
|
||||
lbl.text = 'Hello Dart!';
|
||||
document.body.nodes.add(lbl);
|
||||
});
|
||||
}
|
||||
|
12
utils/tests/testrunner/layout_tests/web/layout_test.html
Executable file
12
utils/tests/testrunner/layout_tests/web/layout_test.html
Executable file
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testdriver tests</title>
|
||||
</head>
|
||||
<body>
|
||||
<script type="application/dart" src="browser_test.dart"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
24
utils/tests/testrunner/non_browser_tests/non_browser_test.dart
Executable file
24
utils/tests/testrunner/non_browser_tests/non_browser_test.dart
Executable file
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
library testrunner_test;
|
||||
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
foo(bool x) => x;
|
||||
|
||||
main() {
|
||||
group('group1', () {
|
||||
test('test1', () {
|
||||
expect(true, isFalse);
|
||||
});
|
||||
});
|
||||
group('group2', () {
|
||||
test('test2', () {
|
||||
foo(3);
|
||||
expect(true, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
16
utils/tests/testrunner/non_browser_tests/non_browser_toast.dart
Executable file
16
utils/tests/testrunner/non_browser_tests/non_browser_toast.dart
Executable file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
library testrunner_test;
|
||||
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
main() {
|
||||
group('foo', () {
|
||||
test('bar', () {
|
||||
expect(true, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
4
utils/tests/testrunner/non_browser_tests/pubspec.yaml
Executable file
4
utils/tests/testrunner/non_browser_tests/pubspec.yaml
Executable file
|
@ -0,0 +1,4 @@
|
|||
name: non_browser_tests
|
||||
dependencies:
|
||||
unittest: any
|
||||
|
4
utils/tests/testrunner/pubspec.yaml
Executable file
4
utils/tests/testrunner/pubspec.yaml
Executable file
|
@ -0,0 +1,4 @@
|
|||
name: non_browser_tests
|
||||
dependencies:
|
||||
unittest: any
|
||||
|
1
utils/tests/testrunner/test.txt
Normal file
1
utils/tests/testrunner/test.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Hello world!
|
1
utils/tests/testrunner/testconfig
Normal file
1
utils/tests/testrunner/testconfig
Normal file
|
@ -0,0 +1 @@
|
|||
--checked
|
522
utils/tests/testrunner/testrunner_test.dart
Executable file
522
utils/tests/testrunner/testrunner_test.dart
Executable file
|
@ -0,0 +1,522 @@
|
|||
// Copyright (c) 2013, 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.
|
||||
|
||||
library testrunner_test;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
var dart;
|
||||
var debug = false;
|
||||
|
||||
Future runTestrunner(command, List<String> args,
|
||||
List<String> stdout, List<String> stderr) {
|
||||
if (debug) {
|
||||
print("Running $command ${args.join(' ')}");
|
||||
}
|
||||
return Process.run(command, args).then((ProcessResult result) {
|
||||
var lineEndings = new RegExp("\r\n|\n");
|
||||
stdout.addAll(result.stdout.trim().split(lineEndings));
|
||||
stderr.addAll(result.stderr.trim().split(lineEndings));
|
||||
})
|
||||
.catchError((e) {
|
||||
stderr.add("Error starting process:");
|
||||
stderr.add(" Command: $command");
|
||||
stderr.add(" Error: ${e}");
|
||||
completer.complete(-1);
|
||||
});
|
||||
}
|
||||
|
||||
// Useful utility for debugging test failures.
|
||||
void dump(label, list) {
|
||||
if (!debug) return;
|
||||
print('\n@=[ $label ]=============================\n');
|
||||
for (var i = 0; i < list.length; i++)
|
||||
print('@ ${list[i]}\n');
|
||||
print('------------------------------------------\n');
|
||||
}
|
||||
|
||||
int stringCompare(String s1, String s2) => s1.compareTo(s2);
|
||||
|
||||
Future runTest(
|
||||
List<String> args,
|
||||
List<String> expected_stdout,
|
||||
{List<String> expected_stderr, sort: false}) {
|
||||
var stdout = new List<String>();
|
||||
var stderr = new List<String>();
|
||||
for (var i = 0; i < expected_stdout.length; i++) {
|
||||
expected_stdout[i] = expected_stdout[i].
|
||||
replaceAll('/', Platform.pathSeparator);
|
||||
}
|
||||
if (debug) {
|
||||
args.insert(1, "--log=stderr");
|
||||
}
|
||||
var rtn = runTestrunner(dart, args, stdout, stderr);
|
||||
rtn.then((_) {
|
||||
dump('stderr', stderr);
|
||||
dump('stdout', stdout);
|
||||
|
||||
if (expected_stderr != null) {
|
||||
expect(stderr.length, orderedEquals(expected_stderr));
|
||||
}
|
||||
var i, l = 0, matched = 0;
|
||||
if (sort) {
|
||||
stdout.sort(stringCompare);
|
||||
expected_stdout.sort(stringCompare);
|
||||
}
|
||||
for (i = 0; i < stdout.length; i++) {
|
||||
if (!stdout[i].startsWith('@')) {
|
||||
if (expected_stdout.length <= l) {
|
||||
fail("Extra text in output: ${stdout[i]}");
|
||||
return;
|
||||
}
|
||||
var actual = stdout[i].trim();
|
||||
if (debug) {
|
||||
print("Compare <$actual> and <${expected_stdout[l]}>");
|
||||
}
|
||||
if (expected_stdout[l].startsWith('*')) {
|
||||
expect(actual, endsWith(expected_stdout[l].substring(1)));
|
||||
} else if (expected_stdout[l].startsWith('?')) {
|
||||
var pat = expected_stdout[l].substring(1);
|
||||
if (Platform.operatingSystem == 'windows') {
|
||||
// The joys of Windows...
|
||||
pat = pat.replaceAll('\\','\\\\');
|
||||
}
|
||||
expect(actual, matches(pat));
|
||||
} else {
|
||||
expect(actual, expected_stdout[l]);
|
||||
}
|
||||
++l;
|
||||
}
|
||||
}
|
||||
if (l < expected_stdout.length) {
|
||||
fail("Only matched $l of ${expected_stdout.length} lines");
|
||||
}
|
||||
});
|
||||
return rtn;
|
||||
}
|
||||
|
||||
// A useful function to quickly disable a group of tests; just
|
||||
// replace group() with skip_group().
|
||||
skip_group(_1,_2) {}
|
||||
|
||||
main() {
|
||||
var opt = new Options();
|
||||
dart = opt.executable;
|
||||
var idx = dart.indexOf('dart-sdk');
|
||||
if (idx < 0) {
|
||||
print("Please run using the dart executable from the Dart SDK");
|
||||
exit(-1);
|
||||
}
|
||||
var _ = Platform.pathSeparator;
|
||||
var testrunner = '../../testrunner/testrunner.dart'
|
||||
.replaceAll('/', Platform.pathSeparator);
|
||||
|
||||
group("list tests", () {
|
||||
test('list file', () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--list-files',
|
||||
'non_browser_tests' ],
|
||||
[ '?.*/non_browser_tests/non_browser_test.dart' ]);
|
||||
});
|
||||
test('list files', () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--sort',
|
||||
'--list-files',
|
||||
'--test-file-pattern=.dart\$' ],
|
||||
[ '*browser_tests/web/browser_test.dart',
|
||||
'*http_client_tests/http_client_test.dart',
|
||||
'*layout_tests/web/layout_test.dart',
|
||||
'*non_browser_tests/non_browser_test.dart',
|
||||
'*non_browser_tests/non_browser_toast.dart',
|
||||
'*/testrunner_test.dart' ]
|
||||
);
|
||||
});
|
||||
test('list files', () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--list-files',
|
||||
'--test-file-pattern=.dart\$',
|
||||
'non_browser_tests' ],
|
||||
[ '*non_browser_tests/non_browser_test.dart',
|
||||
'*non_browser_tests/non_browser_toast.dart' ],
|
||||
sort:true
|
||||
);
|
||||
});
|
||||
test('list groups', () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--list-groups',
|
||||
'non_browser_tests' ],
|
||||
[ '*non_browser_tests/non_browser_test.dart group1',
|
||||
'*non_browser_tests/non_browser_test.dart group2']);
|
||||
});
|
||||
test('list tests', () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--list-tests',
|
||||
'non_browser_tests' ],
|
||||
[ '*non_browser_tests/non_browser_test.dart group1 test1',
|
||||
'*non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
});
|
||||
|
||||
group("vm", () {
|
||||
test("vm without timing info", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1'
|
||||
' Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
|
||||
test("vm with timing info", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--time',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL [0-9.]+s .*/non_browser_tests/non_browser_test.dart group1'
|
||||
' test1 Expected: false',
|
||||
'?PASS [0-9.]+s .*/non_browser_tests/non_browser_test.dart group2'
|
||||
' test2' ]);
|
||||
});
|
||||
});
|
||||
|
||||
group("selection", () {
|
||||
test("--include", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--include=group1',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1 '
|
||||
'Expected: false' ]);
|
||||
});
|
||||
|
||||
test("--exclude", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--exclude=group1',
|
||||
'non_browser_tests' ],
|
||||
[ '?PASS .*/non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
|
||||
test("test file pattern", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--test-file-pattern=toast',
|
||||
'non_browser_tests' ],
|
||||
[ '?PASS .*/non_browser_tests/non_browser_toast.dart foo bar' ]);
|
||||
});
|
||||
});
|
||||
|
||||
group("stop on failure tests", () {
|
||||
test("without stop", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--sort',
|
||||
'--tasks=1',
|
||||
'--test-file-pattern=.dart\$',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2',
|
||||
'?PASS .*/non_browser_tests/non_browser_toast.dart foo bar' ]);
|
||||
});
|
||||
test("with stop", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--sort',
|
||||
'--tasks=1',
|
||||
'--test-file-pattern=.dart\$',
|
||||
'--stop-on-failure',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
});
|
||||
|
||||
group("output control", () {
|
||||
test("summary test", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--summary',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2',
|
||||
'',
|
||||
'?.*/non_browser_tests/non_browser_test.dart: '
|
||||
'1 PASSED, 1 FAILED, 0 ERRORS' ]);
|
||||
});
|
||||
|
||||
test('list tests with custom format', () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--list-tests',
|
||||
'--list-format="<FILENAME><TESTNAME>"',
|
||||
'non_browser_tests' ],
|
||||
[ '?.*/non_browser_tests/non_browser_test.dart test1',
|
||||
'?.*/non_browser_tests/non_browser_test.dart test2' ]);
|
||||
});
|
||||
|
||||
test("custom message formatting", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--pass-format=YIPPEE! <GROUPNAME><TESTNAME>',
|
||||
'--fail-format=EPIC FAIL! <GROUPNAME><TESTNAME>',
|
||||
'non_browser_tests' ],
|
||||
[ 'EPIC FAIL! group1 test1', 'YIPPEE! group2 test2' ]);
|
||||
});
|
||||
});
|
||||
|
||||
test("checked mode test", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--checked',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
"?FAIL .*/non_browser_tests/non_browser_test.dart group2 test2 "
|
||||
"Caught type 'int' is not a subtype of type 'bool' of 'x'." ]);
|
||||
});
|
||||
|
||||
group("browser", () {
|
||||
test("native test", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--runtime=drt-dart',
|
||||
'browser_tests' ],
|
||||
[ '?FAIL .*/browser_tests/web/browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
'?PASS .*/browser_tests/web/browser_test.dart group2 test2' ]);
|
||||
});
|
||||
|
||||
test("compiled test", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--runtime=drt-js',
|
||||
'browser_tests' ],
|
||||
[ '?FAIL .*/browser_tests/web/browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
'?PASS .*/browser_tests/web/browser_test.dart group2 test2' ]);
|
||||
});
|
||||
});
|
||||
|
||||
group("textual layout tests", () {
|
||||
group("drt-dart", () {
|
||||
test("no baseline", () {
|
||||
var f = new File("layout_tests/web/layout_test/layout.txt");
|
||||
if (f.existsSync()) {
|
||||
f.deleteSync();
|
||||
}
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--layout-text',
|
||||
'layout_tests' ],
|
||||
[ '?FAIL .*/layout_tests/web/layout_test.dart layout '
|
||||
'No expectation file' ]);
|
||||
});
|
||||
test("create baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--layout-text',
|
||||
'--regenerate',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
test("test baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--layout-text',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
});
|
||||
group("drt-js", () {
|
||||
test("no baseline", () {
|
||||
var f = new File("layout_tests/web/layout_test/layout.txt");
|
||||
if (f.existsSync()) {
|
||||
f.deleteSync();
|
||||
}
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--layout-text',
|
||||
'layout_tests' ],
|
||||
[ '?FAIL .*/layout_tests/web/layout_test.dart layout '
|
||||
'No expectation file' ]);
|
||||
});
|
||||
test("create baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--layout-text',
|
||||
'--regenerate',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
test("test baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--layout-text',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("pixel layout tests", () {
|
||||
group("drt-dart", () {
|
||||
test("no baseline", () {
|
||||
var f = new File("layout_tests/web/layout_test/layout.png");
|
||||
if (f.existsSync()) {
|
||||
f.deleteSync();
|
||||
}
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--layout-pixel',
|
||||
'layout_tests' ],
|
||||
[ '?FAIL .*/layout_tests/web/layout_test.dart layout '
|
||||
'No expectation file' ]);
|
||||
});
|
||||
test("create baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--layout-pixel',
|
||||
'--regenerate',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
test("test baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--layout-pixel',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
// TODO(gram): Should add a test that changes a byte of the
|
||||
// expectation .png.
|
||||
});
|
||||
group("drt-js", () {
|
||||
test("no baseline", () {
|
||||
var f = new File("layout_tests/web/layout_test/layout.png");
|
||||
if (f.existsSync()) {
|
||||
f.deleteSync();
|
||||
}
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--layout-pixel',
|
||||
'layout_tests' ],
|
||||
[ '?FAIL .*/layout_tests/web/layout_test.dart layout '
|
||||
'No expectation file' ]);
|
||||
});
|
||||
test("create baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--layout-pixel',
|
||||
'--regenerate',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
test("test baseline", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--layout-pixel',
|
||||
'layout_tests' ],
|
||||
[ '?PASS .*/layout_tests/web/layout_test.dart layout' ]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group("run in isolate", () {
|
||||
test("vm", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=vm',
|
||||
'--recurse',
|
||||
'--isolate',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1'
|
||||
' Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
test("drt-dart", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-dart',
|
||||
'--recurse',
|
||||
'--isolate',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1'
|
||||
' Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
test("drt-js", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--runtime=drt-js',
|
||||
'--recurse',
|
||||
'--isolate',
|
||||
'non_browser_tests' ],
|
||||
[ '?FAIL .*/non_browser_tests/non_browser_test.dart group1 test1 '
|
||||
'Expected: false',
|
||||
'?PASS .*/non_browser_tests/non_browser_test.dart group2 test2' ]);
|
||||
});
|
||||
});
|
||||
|
||||
group("embedded server", () {
|
||||
test("get test", () {
|
||||
return runTest(
|
||||
[ testrunner,
|
||||
'--recurse',
|
||||
'--server',
|
||||
'--port=3456',
|
||||
'--root=${new Directory.current().path}',
|
||||
'http_client_tests' ],
|
||||
[ '?PASS .*/http_client_tests/http_client_test.dart test1',
|
||||
'?PASS .*/http_client_tests/http_client_test.dart test2' ]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in a new issue