Chromium Code Reviews| Index: pkg/barback/test/utils.dart |
| diff --git a/pkg/barback/test/utils.dart b/pkg/barback/test/utils.dart |
| index f14ff3d4595d5cabd5a0503ab3205d82bf4a2862..0cca0553068f414477529eb1f5e0f8ddc0f5c1df 100644 |
| --- a/pkg/barback/test/utils.dart |
| +++ b/pkg/barback/test/utils.dart |
| @@ -10,8 +10,11 @@ import 'dart:io'; |
| import 'package:barback/barback.dart'; |
| import 'package:barback/src/asset_graph.dart'; |
| +import 'package:barback/src/asset_graph_manager.dart'; |
| +import 'package:barback/src/utils.dart'; |
| import 'package:pathos/path.dart' as pathos; |
| import 'package:scheduled_test/scheduled_test.dart'; |
| +import 'package:stack_trace/stack_trace.dart'; |
| // TODO(rnystrom): Get rid of this or find a better path for it. |
| import '../../../sdk/lib/_internal/pub/test/command_line_config.dart'; |
| @@ -19,7 +22,7 @@ import '../../../sdk/lib/_internal/pub/test/command_line_config.dart'; |
| var _configured = false; |
| MockProvider _provider; |
| -AssetGraph _graph; |
| +AssetGraphManager _graphManager; |
| /// Calls to [buildShouldSucceed] and [buildShouldFail] set expectations on |
| /// successive [BuildResult]s from [_graph]. This keeps track of how many calls |
| @@ -44,12 +47,16 @@ void initConfig() { |
| /// one. If it's a [Map], each key should be a string that can be parsed to an |
| /// [AssetId] and the value should be a string defining the contents of that |
| /// asset. |
| -void initGraph([assets, Iterable<Iterable<Transformer>> transformers]) { |
| +/// |
| +/// [transformers] is a map from package names to the transformers for each |
| +/// package. |
| +void initGraph([assets, |
| + Map<String, Iterable<Iterable<Transformer>>> transformers]) { |
| if (assets == null) assets = []; |
| - if (transformers == null) transformers = []; |
| + if (transformers == null) transformers = {}; |
| - _provider = new MockProvider(assets); |
| - _graph = new AssetGraph(_provider, transformers); |
| + _provider = new MockProvider(assets, transformers); |
| + _graphManager = new AssetGraphManager(_provider); |
| _nextBuildResult = 0; |
| } |
| @@ -64,7 +71,7 @@ void updateSources(Iterable assets) { |
| return asset; |
| }); |
| - _graph.updateSources(assets); |
| + _graphManager.updateSources(assets); |
| } |
| /// Removes [assets] from the current [AssetProvider]. |
| @@ -78,7 +85,7 @@ void removeSources(Iterable assets) { |
| return asset; |
| }); |
| - _graph.removeSources(assets); |
| + _graphManager.removeSources(assets); |
| } |
| /// Schedules a change to the contents of an asset identified by [name] to |
| @@ -96,7 +103,7 @@ void modifyAsset(String name, String contents) { |
| /// All asset requests that the [AssetGraph] makes to the provider after this |
| /// will not complete until [resumeProvider] is called. |
| void pauseProvider() { |
| - schedule(() =>_provider._pause(), "pause provider"); |
| + schedule(() => _provider._pause(), "pause provider"); |
| } |
| /// Schedules an unpause of the provider after a call to [pauseProvider] and |
| @@ -105,9 +112,35 @@ void resumeProvider() { |
| schedule(() => _provider._resume(), "resume provider"); |
| } |
| +/// Asserts that the build should not terminate at this point in the schedule. |
|
Bob Nystrom
2013/07/11 23:03:40
"should not terminate at this point" is unclear. D
nweiz
2013/07/15 22:11:44
Clarified.
|
| +/// |
| +/// This uses the same build counter as [buildShouldSucceed] and |
| +/// [buildShouldFail], so those can be used to validate build results before and |
| +/// after this. |
| +void buildShouldNotBeDone() { |
| + var resultAllowed = false; |
| + var trace = new Trace.current(); |
| + _graphManager.results.elementAt(_nextBuildResult).then((result) { |
| + if (resultAllowed) return; |
| + |
| + currentSchedule.signalError( |
| + new Exception("Expected build not to terminate " |
| + "here, but it terminated with result: $result"), trace); |
| + }).catchError((error) { |
| + if (resultAllowed) return; |
| + currentSchedule.signalError(error); |
| + }); |
| + |
| + schedule(() { |
| + return pumpEventQueue().then((_) { |
|
Bob Nystrom
2013/07/11 23:03:40
Explain/document this.
nweiz
2013/07/15 22:11:44
Done.
|
| + resultAllowed = true; |
| + }); |
| + }, "ensuring build doesn't terminate"); |
| +} |
| + |
| /// Expects that the next [BuildResult] is a build success. |
| void buildShouldSucceed() { |
| - expect(_graph.results.elementAt(_nextBuildResult++).then((result) { |
| + expect(_getNextBuildResult().then((result) { |
| expect(result.succeeded, isTrue); |
| }), completes); |
| } |
| @@ -118,7 +151,7 @@ void buildShouldSucceed() { |
| /// build to fail. Every matcher is expected to match an error, but the order of |
| /// matchers is unimportant. |
| void buildShouldFail(List matchers) { |
| - expect(_graph.results.elementAt(_nextBuildResult++).then((result) { |
| + expect(_getNextBuildResult().then((result) { |
| expect(result.succeeded, isFalse); |
| expect(result.errors.length, equals(matchers.length)); |
| for (var matcher in matchers) { |
| @@ -127,12 +160,15 @@ void buildShouldFail(List matchers) { |
| }), completes); |
| } |
| +Future<BuildResult> _getNextBuildResult() => |
| + _graphManager.results.elementAt(_nextBuildResult++); |
| + |
| /// Pauses the schedule until the currently running build completes. |
| /// |
| /// Validates that the build completed successfully. |
| void waitForBuild() { |
| schedule(() { |
| - return _graph.results.first.then((result) { |
| + return _graphManager.results.first.then((result) { |
| expect(result.succeeded, isTrue); |
| }); |
| }, "wait for build"); |
| @@ -151,7 +187,7 @@ void expectAsset(String name, [String contents]) { |
| } |
| schedule(() { |
| - return _graph.getAssetById(id).then((asset) { |
| + return _graphManager.getAssetById(id).then((asset) { |
| // TODO(rnystrom): Make an actual Matcher class for this. |
| expect(asset, new isInstanceOf<MockAsset>()); |
| expect(asset.id, equals(id)); |
| @@ -167,7 +203,7 @@ void expectNoAsset(String name) { |
| // Make sure the future gets the error. |
| schedule(() { |
| - return _graph.getAssetById(id).then((asset) { |
| + return _graphManager.getAssetById(id).then((asset) { |
| fail("Should have thrown error but got $asset."); |
| }).catchError((error) { |
| expect(error, new isInstanceOf<AssetNotFoundException>()); |
| @@ -176,22 +212,6 @@ void expectNoAsset(String name) { |
| }, "get asset $name"); |
| } |
| -/// Schedules an expectation that [graph] will have an error on an asset |
| -/// matching [name] for missing [input]. |
| -Future expectMissingInput(AssetGraph graph, String name, String input) { |
| - var missing = new AssetId.parse(input); |
| - |
| - // Make sure the future gets the error. |
| - schedule(() { |
| - return graph.getAssetById(new AssetId.parse(name)).then((asset) { |
| - fail("Should have thrown error but got $asset."); |
| - }).catchError((error) { |
| - expect(error, new isInstanceOf<MissingInputException>()); |
| - expect(error.id, equals(missing)); |
| - }); |
| - }, "get missing input on $name"); |
| -} |
| - |
| /// Returns a matcher for an [AssetNotFoundException] with the given [id]. |
| Matcher isAssetNotFoundException(String name) { |
| var id = new AssetId.parse(name); |
| @@ -220,7 +240,7 @@ Matcher isMissingInputException(String name) { |
| class MockProvider implements AssetProvider { |
| Iterable<String> get packages => _packages.keys; |
| - final _packages = new Map<String, List<MockAsset>>(); |
| + Map<String, _MockPackage> _packages; |
| /// The completer that [getAsset()] is waiting on to complete when paused. |
| /// |
| @@ -240,26 +260,38 @@ class MockProvider implements AssetProvider { |
| _pauseCompleter = null; |
| } |
| - MockProvider(assets) { |
| + MockProvider(assets, |
| + Map<String, Iterable<Iterable<Transformer>>> transformers) { |
|
Bob Nystrom
2013/07/11 23:03:40
How about also allowing this to just be a nested l
nweiz
2013/07/15 22:11:44
I thought about that, but it seemed weird to assum
Bob Nystrom
2013/07/16 17:36:01
Yeah, I was actually thinking about doing the form
nweiz
2013/07/16 19:39:39
I'll leave it for now, but let's keep the idea in
|
| + var assetList; |
| if (assets is Map) { |
| - assets.forEach((asset, contents) { |
| + assetList = assets.keys.map((asset) { |
| var id = new AssetId.parse(asset); |
| - var package = _packages.putIfAbsent(id.package, () => []); |
| - package.add(new MockAsset(id, contents)); |
| + return new MockAsset(id, assets[asset]); |
| }); |
| } else if (assets is Iterable) { |
| - for (var asset in assets) { |
| + assetList = assets.map((asset) { |
| var id = new AssetId.parse(asset); |
| - var package = _packages.putIfAbsent(id.package, () => []); |
| var contents = pathos.basenameWithoutExtension(id.path); |
| - package.add(new MockAsset(id, contents)); |
| - } |
| + return new MockAsset(id, contents); |
| + }); |
| } |
| + |
| + _packages = mapMapValues(groupBy(assetList, (asset) => asset.id.package), |
| + (package, assets) { |
| + var packageTransformers = transformers[package]; |
| + if (packageTransformers == null) packageTransformers = []; |
| + return new _MockPackage(assets, packageTransformers.toList()); |
| + }); |
| + |
| + // If there are no assets or transformers, add a dummy package. This better |
| + // simulates the real world, where there'll always be at least the |
| + // entrypoint package. |
| + if (_packages.isEmpty) _packages = {"app": new _MockPackage([], [])}; |
| } |
| void _modifyAsset(String name, String contents) { |
| var id = new AssetId.parse(name); |
| - var asset = _packages[id.package].firstWhere((a) => a.id == id); |
| + var asset = _packages[id.package].assets.firstWhere((a) => a.id == id); |
| asset.contents = contents; |
| } |
| @@ -268,7 +300,15 @@ class MockProvider implements AssetProvider { |
| throw new UnimplementedError("Doesn't handle 'within' yet."); |
| } |
| - return _packages[package].map((asset) => asset.id); |
| + return _packages[package].assets.map((asset) => asset.id); |
| + } |
| + |
| + Iterable<Iterable<Transformer>> getTransformers(String package) { |
| + var mockPackage = _packages[package]; |
| + if (mockPackage == null) { |
| + throw new ArgumentError("No package named $package."); |
| + } |
| + return mockPackage.transformers; |
| } |
| Future<Asset> getAsset(AssetId id) { |
| @@ -283,12 +323,20 @@ class MockProvider implements AssetProvider { |
| var package = _packages[id.package]; |
| if (package == null) throw new AssetNotFoundException(id); |
| - return package.firstWhere((asset) => asset.id == id, |
| + return package.assets.firstWhere((asset) => asset.id == id, |
| orElse: () => throw new AssetNotFoundException(id)); |
| }); |
| } |
| } |
| +class _MockPackage { |
|
Bob Nystrom
2013/07/11 23:03:40
Document.
nweiz
2013/07/15 22:11:44
Done.
|
| + final List<MockAsset> assets; |
| + final List<List<Transformer>> transformers; |
| + |
| + _MockPackage(this.assets, Iterable<Iterable<Transformer>> transformers) |
| + : transformers = transformers.map((phase) => phase.toList()).toList(); |
| +} |
| + |
| /// A [Transformer] that takes assets ending with one extension and generates |
| /// assets with a given extension. |
| /// |