Index: lib/src/runner/loader.dart |
diff --git a/lib/src/runner/loader.dart b/lib/src/runner/loader.dart |
index b7594db40536b4bc904c9b47078422a91d145e44..6e68ee20aa1ce8c96dec24a9df0be1f2d75fc512 100644 |
--- a/lib/src/runner/loader.dart |
+++ b/lib/src/runner/loader.dart |
@@ -4,30 +4,25 @@ |
import 'dart:async'; |
import 'dart:io'; |
-import 'dart:isolate'; |
import 'package:analyzer/analyzer.dart' hide Configuration; |
import 'package:async/async.dart'; |
import 'package:path/path.dart' as p; |
-import 'package:stack_trace/stack_trace.dart'; |
import '../backend/group.dart'; |
import '../backend/metadata.dart'; |
-import '../backend/test.dart'; |
import '../backend/test_platform.dart'; |
-import '../util/dart.dart' as dart; |
import '../util/io.dart'; |
-import '../util/remote_exception.dart'; |
import '../utils.dart'; |
import 'browser/server.dart'; |
import 'configuration.dart'; |
-import 'hack_load_vm_file_hook.dart'; |
import 'load_exception.dart'; |
import 'load_suite.dart'; |
import 'parse_metadata.dart'; |
+import 'plugin/environment.dart'; |
+import 'plugin/platform.dart'; |
import 'runner_suite.dart'; |
-import 'vm/environment.dart'; |
-import 'vm/isolate_test.dart'; |
+import 'vm/platform.dart'; |
/// A class for finding test files and loading them into a runnable form. |
class Loader { |
@@ -40,6 +35,11 @@ class Loader { |
/// All suites that have been created by the loader. |
final _suites = new Set<RunnerSuite>(); |
+ /// Plugins for loading test suites for various platforms. |
+ /// |
+ /// This includes the built-in [VMPlatform] plugin. |
+ final _platformPlugins = <TestPlatform, PlatformPlugin>{}; |
+ |
/// The server that serves browser test pages. |
/// |
/// This is lazily initialized the first time it's accessed. |
@@ -58,7 +58,19 @@ class Loader { |
/// [root] is the root directory that will be served for browser tests. It |
/// defaults to the working directory. |
Loader(this._config, {String root}) |
- : _root = root == null ? p.current : root; |
+ : _root = root == null ? p.current : root { |
+ registerPlatformPlugin(new VMPlatform(_config)); |
+ } |
+ |
+ /// Registers [plugin] as a plugin for the platforms it defines in |
+ /// [PlatformPlugin.platforms]. |
+ /// |
+ /// This overwrites previous plugins for those platforms. |
+ void registerPlatformPlugin(PlatformPlugin plugin) { |
+ for (var platform in plugin.platforms) { |
+ _platformPlugins[platform] = plugin; |
+ } |
+ } |
/// Loads all test suites in [dir]. |
/// |
@@ -119,17 +131,27 @@ class Loader { |
// Don't load a skipped suite. |
if (metadata.skip) { |
yield new LoadSuite.forSuite(new RunnerSuite( |
- const VMEnvironment(), |
+ const PluginEnvironment(), |
new Group.root([], metadata: metadata), |
path: path, platform: platform)); |
continue; |
} |
var name = (platform.isJS ? "compiling " : "loading ") + path; |
- yield new LoadSuite(name, () { |
- return platform == TestPlatform.vm |
- ? _loadVmFile(path, metadata) |
- : _loadBrowserFile(path, platform, metadata); |
+ yield new LoadSuite(name, () async { |
+ var plugin = _platformPlugins[platform]; |
+ |
+ if (plugin != null) { |
+ try { |
+ return await plugin.load(path, platform, metadata); |
+ } catch (error, stackTrace) { |
+ if (error is LoadException) rethrow; |
+ await new Future.error(new LoadException(path, error), stackTrace); |
+ } |
+ } |
+ |
+ assert(platform.isBrowser); |
+ return _loadBrowserFile(path, platform, metadata); |
}, path: path, platform: platform); |
} |
} |
@@ -141,125 +163,6 @@ class Loader { |
Metadata metadata) async => |
(await _browserServer).loadSuite(path, platform, metadata); |
- /// Load the test suite at [path] in VM isolate. |
- /// |
- /// [metadata] is the suite-level metadata for the test. |
- Future<RunnerSuite> _loadVmFile(String path, Metadata metadata) async { |
- if (loadVMFileHook != null) { |
- var suite = await loadVMFileHook(path, metadata, _config); |
- _suites.add(suite); |
- return suite; |
- } |
- |
- var receivePort = new ReceivePort(); |
- |
- var isolate; |
- try { |
- if (_config.pubServeUrl != null) { |
- var url = _config.pubServeUrl.resolveUri( |
- p.toUri(p.relative(path, from: 'test') + '.vm_test.dart')); |
- |
- try { |
- isolate = await Isolate.spawnUri(url, [], { |
- 'reply': receivePort.sendPort, |
- 'metadata': metadata.serialize() |
- }, checked: true); |
- } on IsolateSpawnException catch (error) { |
- if (error.message.contains("OS Error: Connection refused") || |
- error.message.contains("The remote computer refused")) { |
- throw new LoadException(path, |
- "Error getting $url: Connection refused\n" |
- 'Make sure "pub serve" is running.'); |
- } else if (error.message.contains("404 Not Found")) { |
- throw new LoadException(path, |
- "Error getting $url: 404 Not Found\n" |
- 'Make sure "pub serve" is serving the test/ directory.'); |
- } |
- |
- throw new LoadException(path, error); |
- } |
- } else { |
- isolate = await dart.runInIsolate(''' |
-import "package:test/src/backend/metadata.dart"; |
-import "package:test/src/runner/vm/isolate_listener.dart"; |
- |
-import "${p.toUri(p.absolute(path))}" as test; |
- |
-void main(_, Map message) { |
- var sendPort = message['reply']; |
- var metadata = new Metadata.deserialize(message['metadata']); |
- IsolateListener.start(sendPort, metadata, () => test.main); |
-} |
-''', { |
- 'reply': receivePort.sendPort, |
- 'metadata': metadata.serialize() |
- }, packageRoot: p.toUri(_config.packageRoot), checked: true); |
- } |
- } catch (error, stackTrace) { |
- receivePort.close(); |
- if (error is LoadException) rethrow; |
- await new Future.error(new LoadException(path, error), stackTrace); |
- } |
- |
- var completer = new Completer(); |
- |
- var subscription = receivePort.listen((response) { |
- if (response["type"] == "print") { |
- print(response["line"]); |
- } else if (response["type"] == "loadException") { |
- isolate.kill(); |
- completer.completeError( |
- new LoadException(path, response["message"]), |
- new Trace.current()); |
- } else if (response["type"] == "error") { |
- isolate.kill(); |
- var asyncError = RemoteException.deserialize(response["error"]); |
- completer.completeError( |
- new LoadException(path, asyncError.error), |
- asyncError.stackTrace); |
- } else { |
- assert(response["type"] == "success"); |
- completer.complete(response["root"]); |
- } |
- }); |
- |
- try { |
- var suite = new RunnerSuite( |
- const VMEnvironment(), |
- _deserializeGroup(await completer.future), |
- path: path, |
- platform: TestPlatform.vm, |
- os: currentOS, |
- onClose: isolate.kill); |
- _suites.add(suite); |
- return suite; |
- } finally { |
- subscription.cancel(); |
- } |
- } |
- |
- /// Deserializes [group] into a concrete [Group] class. |
- Group _deserializeGroup(Map group) { |
- var metadata = new Metadata.deserialize(group['metadata']); |
- return new Group(group['name'], group['entries'].map((entry) { |
- if (entry['type'] == 'group') return _deserializeGroup(entry); |
- return _deserializeTest(entry); |
- }), |
- metadata: metadata, |
- setUpAll: _deserializeTest(group['setUpAll']), |
- tearDownAll: _deserializeTest(group['tearDownAll'])); |
- } |
- |
- /// Deserializes [test] into a concrete [Test] class. |
- /// |
- /// Returns `null` if [test] is `null`. |
- Test _deserializeTest(Map test) { |
- if (test == null) return null; |
- |
- var metadata = new Metadata.deserialize(test['metadata']); |
- return new IsolateTest(test['name'], metadata, test['sendPort']); |
- } |
- |
/// Close all the browsers that the loader currently has open. |
/// |
/// Note that this doesn't close the loader itself. Browser tests can still be |