| 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:io'; | 6 import 'dart:io'; |
| 7 | 7 |
| 8 import 'package:analyzer/analyzer.dart' hide Configuration; | 8 import 'package:analyzer/analyzer.dart' hide Configuration; |
| 9 import 'package:async/async.dart'; | 9 import 'package:async/async.dart'; |
| 10 import 'package:path/path.dart' as p; | 10 import 'package:path/path.dart' as p; |
| 11 | 11 |
| 12 import '../backend/group.dart'; | 12 import '../backend/group.dart'; |
| 13 import '../backend/metadata.dart'; | 13 import '../backend/metadata.dart'; |
| 14 import '../backend/test_platform.dart'; | 14 import '../backend/test_platform.dart'; |
| 15 import '../util/io.dart'; | 15 import '../util/io.dart'; |
| 16 import '../utils.dart'; | 16 import '../utils.dart'; |
| 17 import 'browser/server.dart'; | 17 import 'browser/platform.dart'; |
| 18 import 'configuration.dart'; | 18 import 'configuration.dart'; |
| 19 import 'load_exception.dart'; | 19 import 'load_exception.dart'; |
| 20 import 'load_suite.dart'; | 20 import 'load_suite.dart'; |
| 21 import 'parse_metadata.dart'; | 21 import 'parse_metadata.dart'; |
| 22 import 'plugin/environment.dart'; | 22 import 'plugin/environment.dart'; |
| 23 import 'plugin/platform.dart'; | 23 import 'plugin/platform.dart'; |
| 24 import 'runner_suite.dart'; | 24 import 'runner_suite.dart'; |
| 25 import 'vm/platform.dart'; | 25 import 'vm/platform.dart'; |
| 26 | 26 |
| 27 /// 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. |
| 28 class Loader { | 28 class Loader { |
| 29 /// The test runner configuration. | 29 /// The test runner configuration. |
| 30 final Configuration _config; | 30 final Configuration _config; |
| 31 | 31 |
| 32 /// The root directory that will be served for browser tests. | 32 /// The root directory that will be served for browser tests. |
| 33 final String _root; | 33 final String _root; |
| 34 | 34 |
| 35 /// All suites that have been created by the loader. | 35 /// All suites that have been created by the loader. |
| 36 final _suites = new Set<RunnerSuite>(); | 36 final _suites = new Set<RunnerSuite>(); |
| 37 | 37 |
| 38 /// Plugins for loading test suites for various platforms. | 38 /// Memoizers for platform plugins, indexed by the platforms they support. |
| 39 final _platformPlugins = <TestPlatform, AsyncMemoizer<PlatformPlugin>>{}; |
| 40 |
| 41 /// The functions to use to load [_platformPlugins]. |
| 39 /// | 42 /// |
| 40 /// This includes the built-in [VMPlatform] plugin. | 43 /// These are passed to the plugins' async memoizers when a plugin is needed. |
| 41 final _platformPlugins = <TestPlatform, PlatformPlugin>{}; | 44 final _platformCallbacks = <TestPlatform, AsyncFunction>{}; |
| 42 | |
| 43 /// The server that serves browser test pages. | |
| 44 /// | |
| 45 /// This is lazily initialized the first time it's accessed. | |
| 46 Future<BrowserServer> get _browserServer { | |
| 47 return _browserServerMemo.runOnce(() { | |
| 48 return BrowserServer.start(_config, root: _root); | |
| 49 }); | |
| 50 } | |
| 51 final _browserServerMemo = new AsyncMemoizer<BrowserServer>(); | |
| 52 | |
| 53 /// The memoizer for running [close] exactly once. | |
| 54 final _closeMemo = new AsyncMemoizer(); | |
| 55 | 45 |
| 56 /// Creates a new loader that loads tests on platforms defined in [_config]. | 46 /// Creates a new loader that loads tests on platforms defined in [_config]. |
| 57 /// | 47 /// |
| 58 /// [root] is the root directory that will be served for browser tests. It | 48 /// [root] is the root directory that will be served for browser tests. It |
| 59 /// defaults to the working directory. | 49 /// defaults to the working directory. |
| 60 Loader(this._config, {String root}) | 50 Loader(this._config, {String root}) |
| 61 : _root = root == null ? p.current : root { | 51 : _root = root == null ? p.current : root { |
| 62 registerPlatformPlugin(new VMPlatform(_config)); | 52 registerPlatformPlugin([TestPlatform.vm], () => new VMPlatform(_config)); |
| 53 registerPlatformPlugin([ |
| 54 TestPlatform.dartium, |
| 55 TestPlatform.contentShell, |
| 56 TestPlatform.chrome, |
| 57 TestPlatform.phantomJS, |
| 58 TestPlatform.firefox, |
| 59 TestPlatform.safari, |
| 60 TestPlatform.internetExplorer |
| 61 ], () => BrowserPlatform.start(_config, root: root)); |
| 63 } | 62 } |
| 64 | 63 |
| 65 /// Registers [plugin] as a plugin for the platforms it defines in | 64 /// Registers a [PlatformPlugin] for [platforms]. |
| 66 /// [PlatformPlugin.platforms]. | 65 /// |
| 66 /// When the runner first requests that a suite be loaded for one of the given |
| 67 /// platforms, this will call [getPlugin] to load the platform plugin. It may |
| 68 /// return either a [PlatformPlugin] or a [Future<PlatformPlugin>]. That |
| 69 /// plugin is then preserved and used to load all suites for all matching |
| 70 /// platforms. |
| 67 /// | 71 /// |
| 68 /// This overwrites previous plugins for those platforms. | 72 /// This overwrites previous plugins for those platforms. |
| 69 void registerPlatformPlugin(PlatformPlugin plugin) { | 73 void registerPlatformPlugin(Iterable<TestPlatform> platforms, getPlugin()) { |
| 70 for (var platform in plugin.platforms) { | 74 var memoizer = new AsyncMemoizer(); |
| 71 _platformPlugins[platform] = plugin; | 75 for (var platform in platforms) { |
| 76 _platformPlugins[platform] = memoizer; |
| 77 _platformCallbacks[platform] = getPlugin; |
| 72 } | 78 } |
| 73 } | 79 } |
| 74 | 80 |
| 75 /// Loads all test suites in [dir]. | 81 /// Loads all test suites in [dir]. |
| 76 /// | 82 /// |
| 77 /// This will load tests from files that match the configuration's filename | 83 /// This will load tests from files that match the configuration's filename |
| 78 /// glob. Any tests that fail to load will be emitted as [LoadException]s. | 84 /// glob. Any tests that fail to load will be emitted as [LoadException]s. |
| 79 /// | 85 /// |
| 80 /// This emits [LoadSuite]s that must then be run to emit the actual | 86 /// This emits [LoadSuite]s that must then be run to emit the actual |
| 81 /// [RunnerSuite]s defined in the file. | 87 /// [RunnerSuite]s defined in the file. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 if (metadata.skip) { | 138 if (metadata.skip) { |
| 133 yield new LoadSuite.forSuite(new RunnerSuite( | 139 yield new LoadSuite.forSuite(new RunnerSuite( |
| 134 const PluginEnvironment(), | 140 const PluginEnvironment(), |
| 135 new Group.root([], metadata: metadata), | 141 new Group.root([], metadata: metadata), |
| 136 path: path, platform: platform)); | 142 path: path, platform: platform)); |
| 137 continue; | 143 continue; |
| 138 } | 144 } |
| 139 | 145 |
| 140 var name = (platform.isJS ? "compiling " : "loading ") + path; | 146 var name = (platform.isJS ? "compiling " : "loading ") + path; |
| 141 yield new LoadSuite(name, () async { | 147 yield new LoadSuite(name, () async { |
| 142 var plugin = _platformPlugins[platform]; | 148 var memo = _platformPlugins[platform]; |
| 143 | 149 |
| 144 if (plugin != null) { | 150 try { |
| 145 try { | 151 var plugin = await memo.runOnce(_platformCallbacks[platform]); |
| 146 return await plugin.load(path, platform, metadata); | 152 var suite = await plugin.load(path, platform, metadata); |
| 147 } catch (error, stackTrace) { | 153 _suites.add(suite); |
| 148 if (error is LoadException) rethrow; | 154 return suite; |
| 149 await new Future.error(new LoadException(path, error), stackTrace); | 155 } catch (error, stackTrace) { |
| 150 } | 156 if (error is LoadException) rethrow; |
| 157 await new Future.error(new LoadException(path, error), stackTrace); |
| 151 } | 158 } |
| 152 | |
| 153 assert(platform.isBrowser); | |
| 154 return _loadBrowserFile(path, platform, metadata); | |
| 155 }, path: path, platform: platform); | 159 }, path: path, platform: platform); |
| 156 } | 160 } |
| 157 } | 161 } |
| 158 | 162 |
| 159 /// Load the test suite at [path] in [platform]. | 163 Future closeEphemeral() async { |
| 160 /// | 164 await Future.wait(_platformPlugins.values.map((memo) async { |
| 161 /// [metadata] is the suite-level metadata for the test. | 165 if (!memo.hasRun) return; |
| 162 Future<RunnerSuite> _loadBrowserFile(String path, TestPlatform platform, | 166 await (await memo.future).closeEphemeral(); |
| 163 Metadata metadata) async => | 167 })); |
| 164 (await _browserServer).loadSuite(path, platform, metadata); | |
| 165 | |
| 166 /// Close all the browsers that the loader currently has open. | |
| 167 /// | |
| 168 /// Note that this doesn't close the loader itself. Browser tests can still be | |
| 169 /// loaded, they'll just spawn new browsers. | |
| 170 Future closeBrowsers() async { | |
| 171 if (!_browserServerMemo.hasRun) return; | |
| 172 await (await _browserServer).closeBrowsers(); | |
| 173 } | 168 } |
| 174 | 169 |
| 175 /// Closes the loader and releases all resources allocated by it. | 170 /// Closes the loader and releases all resources allocated by it. |
| 176 Future close() { | 171 Future close() => _closeMemo.runOnce(() async { |
| 177 return _closeMemo.runOnce(() async { | 172 await Future.wait([ |
| 178 await Future.wait(_suites.map((suite) => suite.close())); | 173 Future.wait(_platformPlugins.values.map((memo) async { |
| 179 _suites.clear(); | 174 if (!memo.hasRun) return; |
| 175 await (await memo.future).close(); |
| 176 })), |
| 177 Future.wait(_suites.map((suite) => suite.close())) |
| 178 ]); |
| 180 | 179 |
| 181 if (!_browserServerMemo.hasRun) return; | 180 _platformPlugins.clear(); |
| 182 await (await _browserServer).close(); | 181 _platformCallbacks.clear(); |
| 183 }); | 182 _suites.clear(); |
| 184 } | 183 }); |
| 184 final _closeMemo = new AsyncMemoizer(); |
| 185 } | 185 } |
| OLD | NEW |