Chromium Code Reviews| Index: pkg/barback/test/utils.dart |
| diff --git a/pkg/barback/test/utils.dart b/pkg/barback/test/utils.dart |
| index 23f8e2a1267320b4297299264186dd5f615bde8f..d49adeaaea1813b582119c6d48bb703bd1e31205 100644 |
| --- a/pkg/barback/test/utils.dart |
| +++ b/pkg/barback/test/utils.dart |
| @@ -5,6 +5,7 @@ |
| library barback.test.utils; |
| import 'dart:async'; |
| +import 'dart:collection'; |
| import 'package:barback/barback.dart'; |
| import 'package:barback/src/asset_graph.dart'; |
| @@ -16,14 +17,157 @@ import '../../../sdk/lib/_internal/pub/test/command_line_config.dart'; |
| var _configured = false; |
| +MockProvider _provider; |
| +AssetGraph _graph; |
| + |
| +/// [BuildResult]s that have been output by the graph before an expectation has |
| +/// consumed them. |
| +/// |
| +/// Since [AssetGraph] starts building immediately in the background, results |
| +/// may start streaming before we've set an explicit expectation. When that |
| +/// occurs, we just queue them up here. |
| +final _buildResults = new Queue<BuildResult>(); |
| + |
| +/// The expectations we have on upcoming [BuildResult]s. |
| +final _buildExpectations = new Queue<Completer<BuildResult>>(); |
| + |
| void initConfig() { |
| if (_configured) return; |
| _configured = true; |
| unittestConfiguration = new CommandLineConfiguration(); |
| } |
| -/// Expects that [graph] will return an asset matching [name] and [contents]. |
| -void expectAsset(AssetGraph graph, String name, [String contents]) { |
| +/// Creates a new [AssetProvider] and [AssetGraph] with the given [assets] and |
| +/// [transformers]. |
| +/// |
| +/// This graph is used internally by most of the other functions in this |
| +/// library so you must call it in the test before calling any of the other |
| +/// functions. These other functions are automatically scheduled, with the |
|
nweiz
2013/06/27 00:19:25
"These other functions" is unclear, especially sin
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +/// exception of [updateSources] and [removeSources]. |
| +/// |
| +/// [assets] may either be an [Iterable] or a [Map]. If an [Iterable], each |
|
nweiz
2013/06/27 00:19:25
"If an" -> "If it's an"
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +/// element may either be an [AssetId] or a string that can be parsed to one. |
| +/// If it's a [Map], each key should be a string that can be parsed to an |
| +/// [AssetId] and the value is a string defining the contents of that asset. |
|
nweiz
2013/06/27 00:19:25
"value is" -> "value should be"
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +void initGraph([assets, Iterable<Iterable<Transformer>> transformers]) { |
| + if (assets == null) assets = []; |
| + if (transformers == null) transformers = []; |
| + |
| + _provider = new MockProvider(assets); |
| + _graph = new AssetGraph(_provider, transformers); |
| + |
| + _graph.results.listen(wrapAsync((result) { |
| + if (_buildExpectations.isEmpty) { |
| + // We aren't waiting for a result yet, so just queue it up for later. |
| + _buildResults.add(result); |
| + } else { |
| + // See if it meets the expectation. |
| + _buildExpectations.removeFirst().complete(result); |
| + } |
| + })); |
| + |
| + currentSchedule.onComplete.schedule(() { |
| + // Discard any unused results. It's OK for a test to not care about some |
| + // build results. |
| + _buildResults.clear(); |
| + }, "clear unused build results"); |
| + |
| + // This library should ensure that you can't move to the next test until all |
| + // build expectations are processed. |
| + assert(_buildExpectations.isEmpty); |
| +} |
| + |
| +/// Updates [assets] in the current [AssetProvider]. |
| +/// |
| +/// Each item in the list may either be an [AssetId] or a string that can be |
| +/// parsed as one. Note that this method is not automatically scheduled, so you |
| +/// will typically wrap it in a call to [schedule] yourself. |
|
nweiz
2013/06/27 00:19:25
"you will typically wrap it" is confusing. Most of
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +/// |
| +void updateSources(Iterable assets) { |
| + // Allow strings as asset IDs. |
| + assets = assets.map((asset) { |
| + if (asset is String) return new AssetId.parse(asset); |
| + return asset; |
| + }); |
| + |
| + _graph.updateSources(assets); |
| +} |
| + |
| +/// Removes [assets] from the current [AssetProvider]. |
| +/// |
| +/// Each item in the list may either be an [AssetId] or a string that can be |
| +/// parsed as one. Note that this method is not automatically scheduled, so you |
| +/// will typically wrap it in a call to [schedule] yourself. |
| +void removeSources(Iterable assets) { |
| + // Allow strings as asset IDs. |
| + assets = assets.map((asset) { |
| + if (asset is String) return new AssetId.parse(asset); |
| + return asset; |
| + }); |
| + |
| + _graph.removeSources(assets); |
| +} |
| + |
| +/// Changes the contents of an asset identified by [name] to [contents]. |
| +/// |
| +/// Does not update it in the graph. |
| +void modifyAsset(String name, String contents) { |
| + schedule(() { |
| + _provider._modifyAsset(name, contents); |
| + }, "modify asset $name"); |
| +} |
| + |
| +/// Pauses the internally created [AssetProvider]. All asset requests that the |
|
nweiz
2013/06/27 00:19:25
Paragraph break
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +/// [AssetGraph] makes to the provider after this will not complete until |
| +/// [resumeProvider] is called. |
| +void pauseProvider() { |
| + schedule(() =>_provider._pause(), "resume provider"); |
|
nweiz
2013/06/27 00:19:25
"resume" -> "pause"
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +} |
| + |
| +/// Unpauses the provider after a call to [pauseProvider] and allows all |
| +/// pending asset loads to finish. |
| +void resumeProvider() { |
| + schedule(() => _provider._resume(), "pause provider"); |
|
nweiz
2013/06/27 00:19:25
"pause" -> "resume"
Bob Nystrom
2013/06/27 17:55:14
Done.
|
| +} |
| + |
| +/// Waits for the next [BuildResult] to be emitted and asserts that it is a |
| +/// build success. |
| +void buildShouldSucceed() { |
| + schedule(() { |
| + return _getNextBuildResult().then((result) { |
| + expect(result.succeeded, isTrue); |
| + }); |
| + }, "wait for build to succeed"); |
| +} |
| + |
| +/// Waits for the next [BuildResult] to be emitted and asserts that it is a |
| +/// build failure. Invokes [callback] with the error (not the result) so that |
| +/// it can provide more precise expectations. |
| +void buildShouldFail(void callback(error)) { |
| + schedule(() { |
| + return _getNextBuildResult().then((result) { |
| + expect(result.succeeded, isFalse); |
| + callback(result.error); |
| + }); |
| + }, "wait for build error"); |
| +} |
| + |
| +Future<BuildResult> _getNextBuildResult() { |
| + if (_buildResults.isNotEmpty) { |
| + return new Future.value(_buildResults.removeFirst()); |
| + } |
| + |
| + // We don't have any results yet, so enqueue the expectation. |
| + var completer = new Completer<BuildResult>(); |
| + _buildExpectations.add(completer); |
| + return completer.future; |
| +} |
| + |
| +/// Expects that the graph will deliver an asset matching [name] and [contents]. |
| +/// |
| +/// If [contents] is omitted, defaults to the asset's filename without an |
| +/// extension (which is the same default that [initGraph] uses). |
| +void expectAsset(String name, [String contents]) { |
| var id = new AssetId.parse(name); |
| if (contents == null) { |
| @@ -31,7 +175,7 @@ void expectAsset(AssetGraph graph, String name, [String contents]) { |
| } |
| schedule(() { |
| - return graph.getAssetById(id).then((asset) { |
| + return _graph.getAssetById(id).then((asset) { |
| // TODO(rnystrom): Make an actual Matcher class for this. |
| expect(asset, new isInstanceOf<MockAsset>()); |
| expect(asset._id.package, equals(id.package)); |
| @@ -41,13 +185,13 @@ void expectAsset(AssetGraph graph, String name, [String contents]) { |
| }, "get asset $name"); |
| } |
| -/// Expects that [graph] will not find an asset matching [name]. |
| -void expectNoAsset(AssetGraph graph, String name) { |
| +/// Expects that the graph will not find an asset matching [name]. |
| +void expectNoAsset(String name) { |
| var id = new AssetId.parse(name); |
| // Make sure the future gets the error. |
| schedule(() { |
| - return graph.getAssetById(id).then((asset) { |
| + return _graph.getAssetById(id).then((asset) { |
| fail("Should have thrown error but got $asset."); |
| }).catchError((error) { |
| expect(error, new isInstanceOf<AssetNotFoundException>()); |
| @@ -56,12 +200,12 @@ void expectNoAsset(AssetGraph graph, String name) { |
| }, "get asset $name"); |
| } |
| -/// Expects that [graph] will have an output file collision error on an asset |
| +/// Expects that the graph will have an output file collision error on an asset |
| /// matching [name]. |
| -Future expectCollision(AssetGraph graph, String name) { |
| +Future expectCollision(String name) { |
| var id = new AssetId.parse(name); |
| return schedule(() { |
| - return graph.results.first.then((result) { |
| + return _graph.results.first.then((result) { |
| expect(result.error, new isInstanceOf<AssetCollisionException>()); |
| expect(result.error.id, equals(id)); |
| }); |
| @@ -90,22 +234,22 @@ class MockProvider implements AssetProvider { |
| final _packages = new Map<String, List<MockAsset>>(); |
| - /// The completer that [getAsset()] is waiting on to complete. |
| + /// The completer that [getAsset()] is waiting on to complete when paused. |
| /// |
| /// If `null` it will return the asset immediately. |
| - Completer _wait; |
| + Completer _pauseCompleter; |
| /// Tells the provider to wait during [getAsset] until [complete()] |
| /// is called. |
| /// |
| /// Lets you test the asynchronous behavior of loading. |
| - void wait() { |
| - _wait = new Completer(); |
| + void _pause() { |
| + _pauseCompleter = new Completer(); |
| } |
| - void complete() { |
| - _wait.complete(); |
| - _wait = null; |
| + void _resume() { |
| + _pauseCompleter.complete(); |
| + _pauseCompleter = null; |
| } |
| MockProvider(assets) { |
| @@ -125,7 +269,7 @@ class MockProvider implements AssetProvider { |
| } |
| } |
| - void modifyAsset(String name, String contents) { |
| + void _modifyAsset(String name, String contents) { |
| var id = new AssetId.parse(name); |
| var asset = _packages[id.package].firstWhere((a) => a._id == id); |
| asset._contents = contents; |
| @@ -141,8 +285,8 @@ class MockProvider implements AssetProvider { |
| Future<Asset> getAsset(AssetId id) { |
| var future; |
| - if (_wait != null) { |
| - future = _wait.future; |
| + if (_pauseCompleter != null) { |
| + future = _pauseCompleter.future; |
| } else { |
| future = new Future.value(); |
| } |