Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(147)

Unified Diff: pkg/barback/test/utils.dart

Issue 21226004: Refactor the barback tests somewhat. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: AssetSet additions Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/barback/test/utils.dart
diff --git a/pkg/barback/test/utils.dart b/pkg/barback/test/utils.dart
index fcda96266b3e226e1f6c97f9b8d42f64fb9f8072..43d3777fbb494963dbe06c30e2c7c56b8336d98a 100644
--- a/pkg/barback/test/utils.dart
+++ b/pkg/barback/test/utils.dart
@@ -10,6 +10,8 @@ import 'dart:io';
import 'package:barback/barback.dart';
import 'package:barback/src/asset_cascade.dart';
+import 'package:barback/src/asset_set.dart';
+import 'package:barback/src/cancelable_future.dart';
import 'package:barback/src/package_graph.dart';
import 'package:barback/src/utils.dart';
import 'package:path/path.dart' as pathos;
@@ -17,6 +19,14 @@ import 'package:scheduled_test/scheduled_test.dart';
import 'package:stack_trace/stack_trace.dart';
import 'package:unittest/compact_vm_config.dart';
+export 'transformer/bad.dart';
+export 'transformer/check_content.dart';
+export 'transformer/create_asset.dart';
+export 'transformer/many_to_one.dart';
+export 'transformer/mock.dart';
+export 'transformer/one_to_many.dart';
+export 'transformer/rewrite.dart';
+
var _configured = false;
MockProvider _provider;
@@ -96,6 +106,15 @@ void modifyAsset(String name, String contents) {
}, "modify asset $name");
}
+/// Schedules an error when loading the asset identified by [name].
Bob Nystrom 2013/07/30 22:25:02 "error" -> "error to be generated".
nweiz 2013/07/30 22:46:21 Done.
+///
+/// Does not update the asset in the graph.
+void setAssetError(String name) {
+ schedule(() {
+ _provider._setAssetError(name);
+ }, "set error for asset $name");
+}
+
/// Schedules a pause of the internally created [PackageProvider].
///
/// All asset requests that the [PackageGraph] makes to the provider after this
@@ -117,31 +136,16 @@ void resumeProvider() {
/// [buildShouldFail], so those can be used to validate build results before and
/// after this.
void buildShouldNotBeDone() {
- var resultAllowed = false;
- var trace = new Trace.current();
- _graph.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(() {
- // Pump the event queue in case the build completes out-of-band after we get
- // here. If it does, we want to signal an error.
- return pumpEventQueue().then((_) {
- resultAllowed = true;
- });
- }, "ensuring build doesn't terminate");
+ _futureShouldNotCompleteUntil(
+ _graph.results.elementAt(_nextBuildResult),
+ schedule(() => pumpEventQueue(), "build should not terminate"),
+ "build");
}
/// Expects that the next [BuildResult] is a build success.
void buildShouldSucceed() {
expect(_getNextBuildResult().then((result) {
+ result.errors.forEach(currentSchedule.signalError);
expect(result.succeeded, isTrue);
}), completes);
}
@@ -190,9 +194,8 @@ void expectAsset(String name, [String contents]) {
schedule(() {
return _graph.getAssetById(id).then((asset) {
// TODO(rnystrom): Make an actual Matcher class for this.
- expect(asset, new isInstanceOf<MockAsset>());
expect(asset.id, equals(id));
- expect(asset.contents, equals(contents));
+ expect(asset.readAsString(), completion(equals(contents)));
});
}, "get asset $name");
}
@@ -213,6 +216,19 @@ void expectNoAsset(String name) {
}, "get asset $name");
}
+/// Schedules an expectation that a [getAssetById] call for the given asset
+/// won't terminate at this point in the schedule.
+void expectAssetDoesNotComplete(String name) {
+ var id = new AssetId.parse(name);
+
+ schedule(() {
+ return _futureShouldNotCompleteUntil(
+ _graph.getAssetById(id),
+ pumpEventQueue(),
+ "asset $id");
+ }, "asset $id should not complete");
+}
+
/// Returns a matcher for an [AssetNotFoundException] with the given [id].
Matcher isAssetNotFoundException(String name) {
var id = new AssetId.parse(name);
@@ -247,12 +263,44 @@ Matcher isInvalidOutputException(String package, String name) {
predicate((error) => error.id == id, 'id is $name'));
}
+/// Returns a matcher for a [MockLoadException] with the given [id].
+Matcher isMockLoadException(String name) {
+ var id = new AssetId.parse(name);
+ return allOf(
+ new isInstanceOf<MockLoadException>(),
+ predicate((error) => error.id == id, 'id is $name'));
Bob Nystrom 2013/07/30 22:25:02 "is" -> "=="
nweiz 2013/07/30 22:46:21 Done.
+}
+
+/// Asserts that [future] shouldn't complete until after [delay] completes.
+///
+/// Once [delay] completes, the output of [future] is ignored, even if it's an
+/// error.
+///
+/// [description] should describe [future].
+Future _futureShouldNotCompleteUntil(Future future, Future delay,
+ String description) {
+ var trace = new Trace.current();
+ var cancelable = new CancelableFuture(future);
+ cancelable.then((result) {
+ currentSchedule.signalError(
+ new Exception("Expected $description not to complete here, but it "
+ "completed with result: $result"),
+ trace);
+ }).catchError((error) {
+ currentSchedule.signalError(error);
+ });
+
+ return delay.then((_) => cancelable.cancel());
+}
+
/// An [AssetProvider] that provides the given set of assets.
class MockProvider implements PackageProvider {
Iterable<String> get packages => _packages.keys;
Map<String, _MockPackage> _packages;
+ final _errors = new Set<AssetId>();
Bob Nystrom 2013/07/30 22:25:02 Document.
nweiz 2013/07/30 22:46:21 Done.
+
/// The completer that [getAsset()] is waiting on to complete when paused.
///
/// If `null` it will return the asset immediately.
@@ -277,13 +325,13 @@ class MockProvider implements PackageProvider {
if (assets is Map) {
assetList = assets.keys.map((asset) {
var id = new AssetId.parse(asset);
- return new MockAsset(id, assets[asset]);
+ return new _MockAsset(id, assets[asset]);
});
} else if (assets is Iterable) {
assetList = assets.map((asset) {
var id = new AssetId.parse(asset);
var contents = pathos.basenameWithoutExtension(id.path);
- return new MockAsset(id, contents);
+ return new _MockAsset(id, contents);
});
}
@@ -291,21 +339,26 @@ class MockProvider implements PackageProvider {
(package, assets) {
var packageTransformers = transformers[package];
if (packageTransformers == null) packageTransformers = [];
- return new _MockPackage(assets, packageTransformers.toList());
+ return new _MockPackage(
+ new AssetSet.from(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([], [])};
+ if (_packages.isEmpty) {
+ _packages = {"app": new _MockPackage(new AssetSet(), [])};
+ }
}
void _modifyAsset(String name, String contents) {
var id = new AssetId.parse(name);
- var asset = _packages[id.package].assets.firstWhere((a) => a.id == id);
- asset.contents = contents;
+ _errors.remove(id);
+ _packages[id.package].assets[id].contents = contents;
}
+ void _setAssetError(String name) => _errors.add(new AssetId.parse(name));
Bob Nystrom 2013/07/30 22:25:02 Document.
nweiz 2013/07/30 22:46:21 Like [_modifyAsset], I didn't document this becaus
+
List<AssetId> listAssets(String package, {String within}) {
if (within != null) {
throw new UnimplementedError("Doesn't handle 'within' yet.");
@@ -323,6 +376,14 @@ class MockProvider implements PackageProvider {
}
Future<Asset> getAsset(AssetId id) {
+ // Eagerly load the asset so we can test an asset's value changing between
+ // when a load starts and when it finishes.
+ var package = _packages[id.package];
+ var asset;
+ if (package != null) asset = package.assets[id];
+
+ var error = _errors.contains(id);
Bob Nystrom 2013/07/30 22:25:02 "hasError"
nweiz 2013/07/30 22:46:21 Done.
+
var future;
if (_pauseCompleter != null) {
future = _pauseCompleter.future;
@@ -331,225 +392,38 @@ class MockProvider implements PackageProvider {
}
return future.then((_) {
- var package = _packages[id.package];
- if (package == null) throw new AssetNotFoundException(id);
-
- return package.assets.firstWhere((asset) => asset.id == id,
- orElse: () => throw new AssetNotFoundException(id));
+ if (error) throw new MockLoadException(id);
+ if (asset == null) throw new AssetNotFoundException(id);
+ return asset;
});
}
}
+/// Error thrown for assets with [setAssetError] set.
+class MockLoadException implements Exception {
+ final AssetId id;
+
+ MockLoadException(this.id);
+
+ String toString() => "Error loading $id.";
+}
+
/// Used by [MockProvider] to keep track of which assets and transformers exist
/// for each package.
class _MockPackage {
- final List<MockAsset> assets;
+ final AssetSet 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.
-///
-/// Appends the output extension to the contents of the input file.
-class RewriteTransformer extends Transformer {
- final String from;
- final String to;
-
- /// The number of times the transformer has been applied.
- int numRuns = 0;
-
- /// The number of currently running transforms.
- int _runningTransforms = 0;
-
- /// The completer that the transform is waiting on to complete.
- ///
- /// If `null` the transform will complete immediately.
- Completer _wait;
-
- /// A future that completes when the first apply of this transformer begins.
- Future get started => _started.future;
- final _started = new Completer();
-
- /// Creates a transformer that rewrites assets whose extension is [from] to
- /// one whose extension is [to].
- ///
- /// [to] may be a space-separated list in which case multiple outputs will be
- /// created for each input.
- RewriteTransformer(this.from, this.to);
-
- /// `true` if any transforms are currently running.
- bool get isRunning => _runningTransforms > 0;
-
- /// Tells the transform to wait during its transformation until [complete()]
- /// is called.
- ///
- /// Lets you test the asynchronous behavior of transformers.
- void wait() {
- _wait = new Completer();
- }
-
- void complete() {
- _wait.complete();
- _wait = null;
- }
-
- Future<bool> isPrimary(Asset asset) {
- return new Future.value(asset.id.extension == ".$from");
- }
-
- Future apply(Transform transform) {
- numRuns++;
- if (!_started.isCompleted) _started.complete();
- _runningTransforms++;
- return transform.primaryInput.then((input) {
- return Future.wait(to.split(" ").map((extension) {
- var id = transform.primaryId.changeExtension(".$extension");
- return input.readAsString().then((content) {
- transform.addOutput(new MockAsset(id, "$content.$extension"));
- });
- })).then((_) {
- if (_wait != null) return _wait.future;
- });
- }).whenComplete(() {
- _runningTransforms--;
- });
- }
-
- String toString() => "$from->$to";
-}
-
-/// A [Transformer] that takes an input asset that contains a comma-separated
-/// list of paths and outputs a file for each path.
-class OneToManyTransformer extends Transformer {
- final String extension;
-
- /// The number of times the transformer has been applied.
- int numRuns = 0;
-
- /// Creates a transformer that consumes assets with [extension].
- ///
- /// That file contains a comma-separated list of paths and it will output
- /// files at each of those paths.
- OneToManyTransformer(this.extension);
-
- Future<bool> isPrimary(Asset asset) {
- return new Future.value(asset.id.extension == ".$extension");
- }
-
- Future apply(Transform transform) {
- numRuns++;
- return transform.primaryInput.then((input) {
- return input.readAsString().then((lines) {
- for (var line in lines.split(",")) {
- var id = new AssetId(transform.primaryId.package, line);
- transform.addOutput(new MockAsset(id, "spread $extension"));
- }
- });
- });
- }
-
- String toString() => "1->many $extension";
-}
-
-/// A transformer that uses the contents of a file to define the other inputs.
-///
-/// Outputs a file with the same name as the primary but with an "out"
-/// extension containing the concatenated contents of all non-primary inputs.
-class ManyToOneTransformer extends Transformer {
- final String extension;
-
- /// The number of times the transformer has been applied.
- int numRuns = 0;
-
- /// Creates a transformer that consumes assets with [extension].
- ///
- /// That file contains a comma-separated list of paths and it will input
- /// files at each of those paths.
- ManyToOneTransformer(this.extension);
-
- Future<bool> isPrimary(Asset asset) {
- return new Future.value(asset.id.extension == ".$extension");
- }
-
- Future apply(Transform transform) {
- numRuns++;
- return transform.primaryInput.then((primary) {
- return primary.readAsString().then((contents) {
- // Get all of the included inputs.
- var inputs = contents.split(",").map((path) {
- var id = new AssetId(transform.primaryId.package, path);
- return transform.getInput(id);
- });
-
- return Future.wait(inputs);
- }).then((inputs) {
- // Concatenate them to one output.
- var output = "";
- return Future.forEach(inputs, (input) {
- return input.readAsString().then((contents) {
- output += contents;
- });
- }).then((_) {
- var id = transform.primaryId.changeExtension(".out");
- transform.addOutput(new MockAsset(id, output));
- });
- });
- });
- }
-
- String toString() => "many->1 $extension";
-}
-
-/// A transformer that throws an exception when run, after generating the
-/// given outputs.
-class BadTransformer extends Transformer {
- /// The error it throws.
- static const ERROR = "I am a bad transformer!";
-
- /// The list of asset names that it should output.
- final List<String> outputs;
-
- BadTransformer(this.outputs);
-
- Future<bool> isPrimary(Asset asset) => new Future.value(true);
- Future apply(Transform transform) {
- return newFuture(() {
- // Create the outputs first.
- for (var output in outputs) {
- var id = new AssetId.parse(output);
- transform.addOutput(new MockAsset(id, output));
- }
-
- // Then fail.
- throw ERROR;
- });
- }
-}
-
-/// A transformer that outputs an asset with the given id.
-class CreateAssetTransformer extends Transformer {
- final String output;
-
- CreateAssetTransformer(this.output);
-
- Future<bool> isPrimary(Asset asset) => new Future.value(true);
-
- Future apply(Transform transform) {
- return newFuture(() {
- transform.addOutput(new MockAsset(new AssetId.parse(output), output));
- });
- }
-}
-
/// An implementation of [Asset] that never hits the file system.
-class MockAsset implements Asset {
+class _MockAsset implements Asset {
Bob Nystrom 2013/07/30 22:25:02 Can you get rid of this entirely?
nweiz 2013/07/30 22:46:21 I considered that, but the fact that MockProvider
Bob Nystrom 2013/07/30 23:14:08 SGTM.
final AssetId id;
String contents;
- MockAsset(this.id, this.contents);
+ _MockAsset(this.id, this.contents);
Future<String> readAsString({Encoding encoding}) =>
new Future.value(contents);
@@ -557,4 +431,4 @@ class MockAsset implements Asset {
Stream<List<int>> read() => throw new UnimplementedError();
String toString() => "MockAsset $id $contents";
-}
+}
« pkg/barback/test/transformer/mock.dart ('K') | « pkg/barback/test/transformer/rewrite.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698