blob: ba0f75d3cc3b2ad9ed4ad94aa8f2889c99b913b2 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/** The default pipeline code for running a test file. */
library pipeline;
import 'dart:isolate';
import 'dart:io';
part 'pipeline_utils.dart';
/**
* The configuration passed in to the pipeline runner; this essentially
* contains all the command line arguments passded to testrunner plus some
* synthesized ones.
*/
Map config;
/** Paths to the various generated temporary files. */
String tempDartFile = null;
String tempHtmlFile = null;
String tempChildDartFile = null;
String tempJsFile = null;
String tempChildJsFile = null;
/**
* Path to the Dart script file referenced from the HTML wrapper (or
* that is compiled to a Javascript file referenced from the HTML wrapper).
*/
String sourceFile = null;
/** Path to the script file referenced from the HTML wrapper (Dart or JS). */
String scriptFile = null;
/** MIME type of the script. */
String scriptType;
/** Process id for the HTTP server. */
int serverId;
/** Port used by HTTP server. */
int serverPort;
/** Root directory for static files used by HTTP server. */
String serverRoot;
/** Path of the HTTP server script. */
String serverPath;
/** Number of attempts we will make to start the HTTP server. */
const int MAX_SERVER_TRIES = 10;
void main() {
port.receive((cfg, replyPort) {
config = cfg;
initPipeline(replyPort);
startHTTPServerStage();
});
}
/** Initial pipeline stage - starts the HTTP server, if appropriate. */
startHTTPServerStage() {
if (config["server"]) {
serverPath = config["testfile"];
// Replace .dart with _server.dart to get test's server file, if any.
var truncLen = serverPath.length - '.dart'.length;
serverPath = '${serverPath.substring(0, truncLen)}_server.dart';
var serverFile = new File(serverPath);
if (!serverFile.existsSync()) {
// No custom server; run the default server.
serverPath = '${config["runnerDir"]}/http_server_runner.dart';
}
if (serverPath != null) {
serverRoot = config["root"];
if (serverRoot == null) {
// Set the root to be the directory containing the test file.
serverRoot = getDirectory(config["testfile"]);
}
if (config["port"] == null) {
// In this case we have to choose a random port and we need
// to see if the server starts successfully on that port.
var r = new Random();
tryStartHTTPServer(r, MAX_SERVER_TRIES);
} else {
serverPort = int.parse(config["port"]);
// Start the HTTP server.
serverId = startProcess(config["dart"],
[ serverPath, '--port=$serverPort', '--root=$serverRoot']);
}
}
}
wrapStage();
}
void tryStartHTTPServer(Random r, int remainingAttempts) {
// Pick a port from 1024 to 32767.
serverPort = 1024 + r.nextInt(32768 - 1024);
logMessage('Trying ${config["dart"]} $serverPath --port=$serverPort '
'--root=$serverRoot');
serverId = startProcess(config["dart"],
[ serverPath, '--port=$serverPort', '--root=$serverRoot'],
(line) {
if (line.startsWith('Server listening')) {
wrapStage();
} else if (remainingAttempts == 0) {
print('Failed to start HTTP server after $MAX_SERVER_TRIES'
' attempts; aborting.');
exit(1);
} else {
tryStartHTTPServer(r, remainingAttempts - 1);
}
});
}
/** Initial pipeline stage - generates Dart and HTML wrapper files. */
wrapStage() {
var tmpDir = config["tempdir"];
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') {
tempHtmlFile = createTempName(tmpDir, testFile, '.html');
if (config["layout"]) {
tempChildDartFile =
createTempName(tmpDir, testFile, '-child.dart');
}
if (config["runtime"] == 'drt-js') {
tempJsFile = createTempName(tmpDir, testFile, '.js');
if (config["layout"]) {
tempChildJsFile =
createTempName(tmpDir, testFile, '-child.js');
}
}
}
// Create the test controller Dart wrapper.
var directives, extras;
if (config["layout"]) {
directives = '''
import 'dart:uri';
import 'dart:io';
import 'dart:math';
part '${config["runnerDir"]}/layout_test_controller.dart';
''';
extras = '''
sourceDir = '${config["expectedDirectory"]}';
baseUrl = 'file://$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';
''';
extras = '''
includeFilters = ${config["include"]};
excludeFilters = ${config["exclude"]};
tprint = (msg) => print('###\$msg');
notifyDone = (e) {};
unittest.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';
''';
extras = '''
includeFilters = ${config["include"]};
excludeFilters = ${config["exclude"]};
tprint = (msg) => query('#console').addText('###\$msg\\n');
notifyDone = (e) => window.postMessage('done', '*');
unittest.testState["port"] = $serverPort;
''';
}
var action = 'process(test.main, unittest.runTests)';
if (config["layout-text"]) {
action = 'runTextLayoutTests()';
} else if (config["layout-pixel"]) {
action = 'runPixelLayoutTests()';
} else if (config["list-tests"]) {
action = 'process(test.main, listTests)';
} else if (config["list-groups"]) {
action = 'process(test.main, listGroups)';
} else if (config["isolate"]) {
action = 'process(test.main, runIsolateTests)';
}
logMessage('Creating $tempDartFile');
writeFile(tempDartFile, '''
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;
}
''');
// 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';
main() {
includeFilters = ${config["include"]};
excludeFilters = ${config["exclude"]};
unittest.testState["port"] = $serverPort;
runTests(test.main);
}
''');
}
// Create the HTML wrapper and compile to Javascript if necessary.
var isJavascript = config["runtime"] == 'drt-js';
if (config["runtime"] == 'drt-dart' || isJavascript) {
var bodyElements, runAsText;
if (config["layout"]) {
sourceFile = tempChildDartFile;
scriptFile = isJavascript ? tempChildJsFile : tempChildDartFile;
bodyElements = '';
} else {
sourceFile = tempDartFile;
scriptFile = isJavascript ? tempJsFile : tempDartFile;
bodyElements = '<div id="container"></div><pre id="console"></pre>';
runAsText = "window.testRunner.dumpAsText();";
}
scriptType = isJavascript ? 'text/javascript' : 'application/dart';
if (config["runtime"] == 'drt-dart' || isJavascript) {
logMessage('Creating $tempHtmlFile');
writeFile(tempHtmlFile, '''
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>$testFile</title>
<link rel="stylesheet" href="${config["runnerDir"]}/testrunner.css">
<script type='text/javascript'>
if (window.testRunner) {
function handleMessage(m) {
if (m.data == 'done') {
window.testRunner.notifyDone();
}
}
window.testRunner.waitUntilDone();
$runAsText
window.addEventListener("message", handleMessage, false);
}
if (!$isJavascript && navigator.webkitStartDart) {
navigator.webkitStartDart();
}
</script>
</head>
<body>
$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>
''');
}
}
compileStage(isJavascript);
}
/** Second stage of pipeline - compiles Dart to Javascript if needed. */
compileStage(isJavascript) {
if (isJavascript) { // Compile the Dart file.
if (config["checked"]) {
runCommand(config["dart2js"],
[ '--enable_checked_mode', '--out=$scriptFile', '$sourceFile' ]).
then(runTestStage);
} else {
runCommand(config["dart2js"],
[ '--out=$scriptFile', '$sourceFile' ]).
then(runTestStage);
}
} else {
runTestStage(0);
}
}
/** Third stage of pipeline - runs the tests. */
runTestStage(_) {
var cmd, args;
if (config["runtime"] == 'vm' || config["layout"]) { // Run the tests.
if (config["checked"]) {
cmd = config["dart"];
args = [ '--enable_asserts', '--enable_type_checks', tempDartFile ];
} else {
cmd = config["dart"];
args = [ tempDartFile ];
}
} else {
cmd = config["drt"];
args = [ '--no-timeout', tempHtmlFile ];
}
runCommand(cmd, args, config["timeout"]).then(cleanupStage);
}
/**
* Final stage of the pipeline - clean up generated files and notify
* the originator that started the isolate.
*/
cleanupStage(exitcode) {
if (config["server"]) { // Stop the HTTP server.
stopProcess(serverId);
}
if (!config["keep-files"]) { // Remove the temporary files.
cleanup(tempDartFile);
cleanup(tempHtmlFile);
cleanup(tempJsFile);
cleanup(tempChildDartFile);
cleanup(tempChildJsFile);
}
completePipeline(exitcode);
}