blob: 521956a3b806fea1f0dd455a246a8275e968242f [file] [log] [blame]
// 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.
// Helper functions and classes for running a set of unittests in a
// remote isolate.
// Used to test Isolate.spawn because dartium/drt does not allow it in the DOM
// isolate.
import "dart:isolate";
import "package:unittest/unittest.dart";
@MirrorsUsed(symbols: "main", targets: "main", override: "*")
import "dart:mirrors";
/**
* Use this function at the beginning of the main method:
*
* void main([args, port]) {
* if (testRemote(main, port)) return;
* // the usual test.
* }
*
* Remember to import unittest using the URI `package:unittest/unittest.dart`.
* Otherwise it won't be sharing the `unittestConfiguration` with this library,
* and the override set below won't work.
*
* Returns `true` if the tests are being run remotely, and
* `false` if the tests should be run locally.
*/
bool testRemote(Function main, SendPort port) {
if (port != null) {
unittestConfiguration = new RemoteConfiguration(port);
return false;
}
var testResponses = new Map<String, List>();
ClosureMirror closure = reflect(main);
LibraryMirror library = closure.function.owner;
var receivePort = new ReceivePort();
void remoteAction(message) {
switch (message[0]) {
case "testStart":
String name = message[1];
testResponses[name] = null;
break;
case "testResult":
case "testResultChanged":
String name = message[1];
testResponses[name] = message;
break;
case "logMessage":
break; // Ignore.
case "summary":
throw message[1]; // Uncaught error.
case "done":
receivePort.close();
_simulateTests(testResponses);
break;
}
}
try {
Isolate.spawnUri(library.uri, null, receivePort.sendPort);
receivePort.listen(remoteAction);
return true;
} catch (e) {
// spawnUri is only supported by dart2js if web workers are available.
// If the spawnUri fails, run the tests locally instead, since we are
// not in a browser anyway.
//
// That is, we assume that either Isolate.spawn or Isolate.spawnUri must
// work, so if spawnUri doesn't work, we can run the tests directly.
receivePort.close();
return false;
}
}
class RemoteConfiguration implements Configuration {
final SendPort _port;
Duration timeout = const Duration(minutes: 2);
RemoteConfiguration(this._port);
bool get autoStart => true;
void onInit() {}
void onStart() {}
void onTestStart(TestCase testCase) {
_port.send(["testStart", testCase.description]);
}
void onTestResult(TestCase testCase) {
_port.send([
"testResult",
testCase.description,
testCase.result,
testCase.message
]);
}
void onTestResultChanged(TestCase testCase) {
_port.send([
"testResultChanged",
testCase.description,
testCase.result,
testCase.message
]);
}
void onLogMessage(TestCase testCase, String message) {
_port.send(["logMessage", testCase.description, message]);
}
void onDone(bool success) {
_port.send(["done", success]);
}
void onSummary(int passed, int failed, int errors, List<TestCase> results,
String uncaughtError) {
if (uncaughtError != null) {
_port.send(["summary", uncaughtError]);
}
}
}
void _simulateTests(Map<String, List> responses) {
// Start all unit tests in the same event.
responses.forEach((name, response) {
test(name, () {
var result = response[2];
var message = response[3];
if (result == FAIL) {
fail(message);
} else if (result == ERROR) {
throw message;
}
});
});
}