| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library test.runner.loader; | 5 library test.runner.loader; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
| 10 | 10 |
| 11 import 'package:analyzer/analyzer.dart'; | 11 import 'package:analyzer/analyzer.dart'; |
| 12 import 'package:path/path.dart' as p; | 12 import 'package:path/path.dart' as p; |
| 13 | 13 |
| 14 import '../backend/metadata.dart'; | 14 import '../backend/metadata.dart'; |
| 15 import '../backend/suite.dart'; | 15 import '../backend/suite.dart'; |
| 16 import '../backend/test_platform.dart'; | 16 import '../backend/test_platform.dart'; |
| 17 import '../util/dart.dart'; | 17 import '../util/dart.dart'; |
| 18 import '../util/io.dart'; | 18 import '../util/io.dart'; |
| 19 import '../util/isolate_wrapper.dart'; |
| 19 import '../util/remote_exception.dart'; | 20 import '../util/remote_exception.dart'; |
| 20 import '../utils.dart'; | 21 import '../utils.dart'; |
| 21 import 'browser/server.dart'; | 22 import 'browser/server.dart'; |
| 22 import 'load_exception.dart'; | 23 import 'load_exception.dart'; |
| 23 import 'parse_metadata.dart'; | 24 import 'parse_metadata.dart'; |
| 24 import 'vm/isolate_test.dart'; | 25 import 'vm/isolate_test.dart'; |
| 25 | 26 |
| 26 /// A class for finding test files and loading them into a runnable form. | 27 /// A class for finding test files and loading them into a runnable form. |
| 27 class Loader { | 28 class Loader { |
| 28 /// All platforms for which tests should be loaded. | 29 /// All platforms for which tests should be loaded. |
| 29 final List<TestPlatform> _platforms; | 30 final List<TestPlatform> _platforms; |
| 30 | 31 |
| 31 /// Whether to enable colors for Dart compilation. | 32 /// Whether to enable colors for Dart compilation. |
| 32 final bool _color; | 33 final bool _color; |
| 33 | 34 |
| 34 /// The package root to use for loading tests, or `null` to use the automatic | 35 /// The package root to use for loading tests, or `null` to use the automatic |
| 35 /// root. | 36 /// root. |
| 36 final String _packageRoot; | 37 final String _packageRoot; |
| 37 | 38 |
| 39 /// The URL for the `pub serve` instance to use to load tests. |
| 40 /// |
| 41 /// This is `null` if tests should be loaded from the filesystem. |
| 42 final Uri _pubServeUrl; |
| 43 |
| 38 /// All isolates that have been spun up by the loader. | 44 /// All isolates that have been spun up by the loader. |
| 39 final _isolates = new Set<Isolate>(); | 45 final _isolates = new Set<Isolate>(); |
| 40 | 46 |
| 41 /// The server that serves browser test pages. | 47 /// The server that serves browser test pages. |
| 42 /// | 48 /// |
| 43 /// This is lazily initialized the first time it's accessed. | 49 /// This is lazily initialized the first time it's accessed. |
| 44 Future<BrowserServer> get _browserServer { | 50 Future<BrowserServer> get _browserServer { |
| 45 if (_browserServerCompleter == null) { | 51 if (_browserServerCompleter == null) { |
| 46 _browserServerCompleter = new Completer(); | 52 _browserServerCompleter = new Completer(); |
| 47 BrowserServer.start(packageRoot: _packageRoot, color: _color) | 53 BrowserServer.start( |
| 54 packageRoot: _packageRoot, |
| 55 pubServeUrl: _pubServeUrl, |
| 56 color: _color) |
| 48 .then(_browserServerCompleter.complete) | 57 .then(_browserServerCompleter.complete) |
| 49 .catchError(_browserServerCompleter.completeError); | 58 .catchError(_browserServerCompleter.completeError); |
| 50 } | 59 } |
| 51 return _browserServerCompleter.future; | 60 return _browserServerCompleter.future; |
| 52 } | 61 } |
| 53 Completer<BrowserServer> _browserServerCompleter; | 62 Completer<BrowserServer> _browserServerCompleter; |
| 54 | 63 |
| 55 /// Creates a new loader. | 64 /// Creates a new loader. |
| 56 /// | 65 /// |
| 57 /// If [packageRoot] is passed, it's used as the package root for all loaded | 66 /// If [packageRoot] is passed, it's used as the package root for all loaded |
| 58 /// tests. Otherwise, the `packages/` directories next to the test entrypoints | 67 /// tests. Otherwise, the `packages/` directories next to the test entrypoints |
| 59 /// will be used. | 68 /// will be used. |
| 60 /// | 69 /// |
| 70 /// If [pubServeUrl] is passed, tests will be loaded from the `pub serve` |
| 71 /// instance at that URL rather than from the filesystem. |
| 72 /// |
| 61 /// If [color] is true, console colors will be used when compiling Dart. | 73 /// If [color] is true, console colors will be used when compiling Dart. |
| 62 Loader(Iterable<TestPlatform> platforms, {String packageRoot, | 74 Loader(Iterable<TestPlatform> platforms, {String packageRoot, |
| 63 bool color: false}) | 75 Uri pubServeUrl, bool color: false}) |
| 64 : _platforms = platforms.toList(), | 76 : _platforms = platforms.toList(), |
| 77 _pubServeUrl = pubServeUrl, |
| 65 _packageRoot = packageRoot, | 78 _packageRoot = packageRoot, |
| 66 _color = color; | 79 _color = color; |
| 67 | 80 |
| 68 /// Loads all test suites in [dir]. | 81 /// Loads all test suites in [dir]. |
| 69 /// | 82 /// |
| 70 /// This will load tests from files that end in "_test.dart". | 83 /// This will load tests from files that end in "_test.dart". |
| 71 Future<List<Suite>> loadDir(String dir) { | 84 Future<List<Suite>> loadDir(String dir) { |
| 72 return Future.wait(new Directory(dir).listSync(recursive: true) | 85 return Future.wait(new Directory(dir).listSync(recursive: true) |
| 73 .map((entry) { | 86 .map((entry) { |
| 74 if (entry is! File) return new Future.value([]); | 87 if (entry is! File) return new Future.value([]); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 93 // the VM's or dart2js's. | 106 // the VM's or dart2js's. |
| 94 metadata = new Metadata(); | 107 metadata = new Metadata(); |
| 95 } on FormatException catch (error) { | 108 } on FormatException catch (error) { |
| 96 throw new LoadException(path, error); | 109 throw new LoadException(path, error); |
| 97 } | 110 } |
| 98 | 111 |
| 99 return Future.wait(_platforms.map((platform) { | 112 return Future.wait(_platforms.map((platform) { |
| 100 return new Future.sync(() { | 113 return new Future.sync(() { |
| 101 if (!metadata.testOn.evaluate(platform, os: currentOS)) return null; | 114 if (!metadata.testOn.evaluate(platform, os: currentOS)) return null; |
| 102 | 115 |
| 116 if (_pubServeUrl != null && !p.isWithin('test', path)) { |
| 117 throw new LoadException(path, |
| 118 'When using "pub serve", all test files must be in test/.'); |
| 119 } |
| 120 |
| 103 if (platform == TestPlatform.chrome) return _loadBrowserFile(path); | 121 if (platform == TestPlatform.chrome) return _loadBrowserFile(path); |
| 104 assert(platform == TestPlatform.vm); | 122 assert(platform == TestPlatform.vm); |
| 105 return _loadVmFile(path); | 123 return _loadVmFile(path); |
| 106 }).then((suite) { | 124 }).then((suite) { |
| 107 if (suite == null) return null; | 125 if (suite == null) return null; |
| 108 return suite.change(metadata: metadata).filter(platform, os: currentOS); | 126 return suite.change(metadata: metadata).filter(platform, os: currentOS); |
| 109 }); | 127 }); |
| 110 })).then((suites) => suites.where((suite) => suite != null).toList()); | 128 })).then((suites) => suites.where((suite) => suite != null).toList()); |
| 111 } | 129 } |
| 112 | 130 |
| 113 /// Load the test suite at [path] in a browser. | 131 /// Load the test suite at [path] in a browser. |
| 114 Future<Suite> _loadBrowserFile(String path) => | 132 Future<Suite> _loadBrowserFile(String path) => |
| 115 _browserServer.then((browserServer) => browserServer.loadSuite(path)); | 133 _browserServer.then((browserServer) => browserServer.loadSuite(path)); |
| 116 | 134 |
| 117 /// Load the test suite at [path] in VM isolate. | 135 /// Load the test suite at [path] in VM isolate. |
| 118 Future<Suite> _loadVmFile(String path) { | 136 Future<Suite> _loadVmFile(String path) { |
| 119 var packageRoot = packageRootFor(path, _packageRoot); | 137 var packageRoot = packageRootFor(path, _packageRoot); |
| 120 var receivePort = new ReceivePort(); | 138 var receivePort = new ReceivePort(); |
| 121 return runInIsolate(''' | 139 |
| 140 return new Future.sync(() { |
| 141 if (_pubServeUrl != null) { |
| 142 var url = _pubServeUrl.resolve( |
| 143 p.withoutExtension(p.relative(path, from: 'test')) + |
| 144 '.vm_test.dart'); |
| 145 return Isolate.spawnUri(url, [], {'reply': receivePort.sendPort}) |
| 146 .then((isolate) => new IsolateWrapper(isolate, () {})) |
| 147 .catchError((error, stackTrace) { |
| 148 if (error is! IsolateSpawnException) throw error; |
| 149 |
| 150 if (error.message.contains("OS Error: Connection refused")) { |
| 151 throw new LoadException(path, |
| 152 "Error getting $url: Connection refused\n" |
| 153 'Make sure "pub serve" is running.'); |
| 154 } else if (error.message.contains("404 Not Found")) { |
| 155 throw new LoadException(path, |
| 156 "Error getting $url: 404 Not Found\n" |
| 157 'Make sure "pub serve" is serving the test/ directory.'); |
| 158 } |
| 159 |
| 160 throw new LoadException(path, error); |
| 161 }); |
| 162 } else { |
| 163 return runInIsolate(''' |
| 122 import "package:test/src/runner/vm/isolate_listener.dart"; | 164 import "package:test/src/runner/vm/isolate_listener.dart"; |
| 123 | 165 |
| 124 import "${p.toUri(p.absolute(path))}" as test; | 166 import "${p.toUri(p.absolute(path))}" as test; |
| 125 | 167 |
| 126 void main(_, Map message) { | 168 void main(_, Map message) { |
| 127 var sendPort = message['reply']; | 169 var sendPort = message['reply']; |
| 128 IsolateListener.start(sendPort, () => test.main); | 170 IsolateListener.start(sendPort, () => test.main); |
| 129 } | 171 } |
| 130 ''', { | 172 ''', { |
| 131 'reply': receivePort.sendPort | 173 'reply': receivePort.sendPort |
| 132 }, packageRoot: packageRoot) | 174 }, packageRoot: packageRoot); |
| 133 .catchError((error, stackTrace) { | 175 } |
| 176 }).catchError((error, stackTrace) { |
| 134 receivePort.close(); | 177 receivePort.close(); |
| 178 if (error is LoadException) throw error; |
| 135 return new Future.error(new LoadException(path, error), stackTrace); | 179 return new Future.error(new LoadException(path, error), stackTrace); |
| 136 }).then((isolate) { | 180 }).then((isolate) { |
| 137 _isolates.add(isolate); | 181 _isolates.add(isolate); |
| 138 return receivePort.first; | 182 return receivePort.first; |
| 139 }).then((response) { | 183 }).then((response) { |
| 140 if (response["type"] == "loadException") { | 184 if (response["type"] == "loadException") { |
| 141 return new Future.error(new LoadException(path, response["message"])); | 185 return new Future.error(new LoadException(path, response["message"])); |
| 142 } else if (response["type"] == "error") { | 186 } else if (response["type"] == "error") { |
| 143 var asyncError = RemoteException.deserialize(response["error"]); | 187 var asyncError = RemoteException.deserialize(response["error"]); |
| 144 return new Future.error( | 188 return new Future.error( |
| (...skipping 12 matching lines...) Expand all Loading... |
| 157 Future close() { | 201 Future close() { |
| 158 for (var isolate in _isolates) { | 202 for (var isolate in _isolates) { |
| 159 isolate.kill(); | 203 isolate.kill(); |
| 160 } | 204 } |
| 161 _isolates.clear(); | 205 _isolates.clear(); |
| 162 | 206 |
| 163 if (_browserServerCompleter == null) return new Future.value(); | 207 if (_browserServerCompleter == null) return new Future.value(); |
| 164 return _browserServer.then((browserServer) => browserServer.close()); | 208 return _browserServer.then((browserServer) => browserServer.close()); |
| 165 } | 209 } |
| 166 } | 210 } |
| OLD | NEW |