| Index: mojo/public/dart/third_party/test/lib/src/runner/loader.dart
|
| diff --git a/mojo/public/dart/third_party/test/lib/src/runner/loader.dart b/mojo/public/dart/third_party/test/lib/src/runner/loader.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..abf22b94f957633d5a77205253ff16402deb2b72
|
| --- /dev/null
|
| +++ b/mojo/public/dart/third_party/test/lib/src/runner/loader.dart
|
| @@ -0,0 +1,254 @@
|
| +// Copyright (c) 2015, 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 test.runner.loader;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:io';
|
| +import 'dart:isolate';
|
| +
|
| +import 'package:analyzer/analyzer.dart';
|
| +import 'package:async/async.dart';
|
| +import 'package:path/path.dart' as p;
|
| +import 'package:stack_trace/stack_trace.dart';
|
| +
|
| +import '../backend/invoker.dart';
|
| +import '../backend/metadata.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 'runner_suite.dart';
|
| +import 'vm/environment.dart';
|
| +import 'vm/isolate_test.dart';
|
| +
|
| +/// A class for finding test files and loading them into a runnable form.
|
| +class Loader {
|
| + /// The test runner configuration.
|
| + final Configuration _config;
|
| +
|
| + /// The root directory that will be served for browser tests.
|
| + final String _root;
|
| +
|
| + /// All suites that have been created by the loader.
|
| + final _suites = new Set<RunnerSuite>();
|
| +
|
| + /// The server that serves browser test pages.
|
| + ///
|
| + /// This is lazily initialized the first time it's accessed.
|
| + Future<BrowserServer> get _browserServer {
|
| + return _browserServerMemo.runOnce(() {
|
| + return BrowserServer.start(_config, root: _root);
|
| + });
|
| + }
|
| + final _browserServerMemo = new AsyncMemoizer<BrowserServer>();
|
| +
|
| + /// The memoizer for running [close] exactly once.
|
| + final _closeMemo = new AsyncMemoizer();
|
| +
|
| + /// Creates a new loader that loads tests on platforms defined in [_config].
|
| + ///
|
| + /// [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;
|
| +
|
| + /// Loads all test suites in [dir].
|
| + ///
|
| + /// This will load tests from files that end in "_test.dart". Any tests that
|
| + /// fail to load will be emitted as [LoadException]s.
|
| + ///
|
| + /// This emits [LoadSuite]s that must then be run to emit the actual
|
| + /// [RunnerSuite]s defined in the file.
|
| + Stream<LoadSuite> loadDir(String dir) {
|
| + return mergeStreams(new Directory(dir).listSync(recursive: true)
|
| + .map((entry) {
|
| + if (entry is! File) return new Stream.fromIterable([]);
|
| +
|
| + if (!entry.path.endsWith("_test.dart")) {
|
| + return new Stream.fromIterable([]);
|
| + }
|
| +
|
| + if (p.split(entry.path).contains('packages')) {
|
| + return new Stream.fromIterable([]);
|
| + }
|
| +
|
| + return loadFile(entry.path);
|
| + }));
|
| + }
|
| +
|
| + /// Loads a test suite from the file at [path].
|
| + ///
|
| + /// This emits [LoadSuite]s that must then be run to emit the actual
|
| + /// [RunnerSuite]s defined in the file.
|
| + ///
|
| + /// This will emit a [LoadException] if the file fails to load.
|
| + Stream<LoadSuite> loadFile(String path) async* {
|
| + var suiteMetadata;
|
| + try {
|
| + suiteMetadata = parseMetadata(path);
|
| + } on AnalyzerErrorGroup catch (_) {
|
| + // Ignore the analyzer's error, since its formatting is much worse than
|
| + // the VM's or dart2js's.
|
| + suiteMetadata = new Metadata();
|
| + } on FormatException catch (error, stackTrace) {
|
| + yield new LoadSuite.forLoadException(
|
| + new LoadException(path, error), stackTrace: stackTrace);
|
| + return;
|
| + }
|
| + suiteMetadata = _config.metadata.merge(suiteMetadata);
|
| +
|
| + if (_config.pubServeUrl != null && !p.isWithin('test', path)) {
|
| + yield new LoadSuite.forLoadException(new LoadException(
|
| + path, 'When using "pub serve", all test files must be in test/.'));
|
| + return;
|
| + }
|
| +
|
| + for (var platform in _config.platforms) {
|
| + if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue;
|
| +
|
| + var metadata = suiteMetadata.forPlatform(platform, os: currentOS);
|
| +
|
| + // Don't load a skipped suite.
|
| + if (metadata.skip) {
|
| + yield new LoadSuite.forSuite(new RunnerSuite(const VMEnvironment(), [
|
| + new LocalTest(path, metadata, () {})
|
| + ], path: path, platform: platform, metadata: metadata));
|
| + continue;
|
| + }
|
| +
|
| + var name = (platform.isJS ? "compiling " : "loading ") + path;
|
| + yield new LoadSuite(name, () {
|
| + return platform == TestPlatform.vm
|
| + ? _loadVmFile(path, metadata)
|
| + : _loadBrowserFile(path, platform, metadata);
|
| + }, platform: platform);
|
| + }
|
| + }
|
| +
|
| + /// Load the test suite at [path] in [platform].
|
| + ///
|
| + /// [metadata] is the suite-level metadata for the test.
|
| + Future<RunnerSuite> _loadBrowserFile(String path, TestPlatform platform,
|
| + 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["tests"]);
|
| + }
|
| + });
|
| +
|
| + try {
|
| + var suite = new RunnerSuite(const VMEnvironment(),
|
| + (await completer.future).map((test) {
|
| + var testMetadata = new Metadata.deserialize(test['metadata']);
|
| + return new IsolateTest(test['name'], testMetadata, test['sendPort']);
|
| + }),
|
| + metadata: metadata,
|
| + path: path,
|
| + platform: TestPlatform.vm,
|
| + os: currentOS,
|
| + onClose: isolate.kill);
|
| + _suites.add(suite);
|
| + return suite;
|
| + } finally {
|
| + subscription.cancel();
|
| + }
|
| + }
|
| +
|
| + /// Closes the loader and releases all resources allocated by it.
|
| + Future close() {
|
| + return _closeMemo.runOnce(() async {
|
| + await Future.wait(_suites.map((suite) => suite.close()));
|
| + _suites.clear();
|
| +
|
| + if (!_browserServerMemo.hasRun) return;
|
| + await (await _browserServer).close();
|
| + });
|
| + }
|
| +}
|
|
|