| Index: mojo/public/dart/third_party/barback/test/utils.dart
|
| diff --git a/mojo/public/dart/third_party/barback/test/utils.dart b/mojo/public/dart/third_party/barback/test/utils.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c24b58c3e58e488a5b360453d673731c47d88b6e
|
| --- /dev/null
|
| +++ b/mojo/public/dart/third_party/barback/test/utils.dart
|
| @@ -0,0 +1,613 @@
|
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library barback.test.utils;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:convert' show Encoding;
|
| +
|
| +import 'package:barback/barback.dart';
|
| +import 'package:barback/src/utils.dart';
|
| +import 'package:barback/src/utils/cancelable_future.dart';
|
| +import 'package:path/path.dart' as pathos;
|
| +import 'package:scheduled_test/scheduled_test.dart';
|
| +import 'package:stack_trace/stack_trace.dart';
|
| +import 'package:unittest/compact_vm_config.dart';
|
| +
|
| +export 'transformer/aggregate_many_to_many.dart';
|
| +export 'transformer/aggregate_many_to_one.dart';
|
| +export 'transformer/bad.dart';
|
| +export 'transformer/bad_log.dart';
|
| +export 'transformer/catch_asset_not_found.dart';
|
| +export 'transformer/check_content.dart';
|
| +export 'transformer/check_content_and_rename.dart';
|
| +export 'transformer/conditionally_consume_primary.dart';
|
| +export 'transformer/create_asset.dart';
|
| +export 'transformer/declare_assets.dart';
|
| +export 'transformer/declaring_aggregate_many_to_many.dart';
|
| +export 'transformer/declaring_aggregate_many_to_one.dart';
|
| +export 'transformer/declaring_bad.dart';
|
| +export 'transformer/declaring_check_content_and_rename.dart';
|
| +export 'transformer/declaring_rewrite.dart';
|
| +export 'transformer/emit_nothing.dart';
|
| +export 'transformer/has_input.dart';
|
| +export 'transformer/lazy_aggregate_many_to_many.dart';
|
| +export 'transformer/lazy_aggregate_many_to_one.dart';
|
| +export 'transformer/lazy_assets.dart';
|
| +export 'transformer/lazy_bad.dart';
|
| +export 'transformer/lazy_check_content_and_rename.dart';
|
| +export 'transformer/lazy_many_to_one.dart';
|
| +export 'transformer/lazy_rewrite.dart';
|
| +export 'transformer/many_to_one.dart';
|
| +export 'transformer/mock.dart';
|
| +export 'transformer/mock_aggregate.dart';
|
| +export 'transformer/one_to_many.dart';
|
| +export 'transformer/rewrite.dart';
|
| +export 'transformer/sync_rewrite.dart';
|
| +
|
| +var _configured = false;
|
| +
|
| +MockProvider _provider;
|
| +Barback _barback;
|
| +
|
| +/// Calls to [buildShouldSucceed] and [buildShouldFail] set expectations on
|
| +/// successive [BuildResult]s from [_barback]. This keeps track of how many
|
| +/// calls have already been made so later calls know which result to look for.
|
| +int _nextBuildResult;
|
| +
|
| +/// Calls to [buildShouldLog] set expectations on successive log entries from
|
| +/// [_barback]. This keeps track of how many calls have already been made so
|
| +/// later calls know which result to look for.
|
| +int _nextLog;
|
| +
|
| +void initConfig() {
|
| + if (_configured) return;
|
| + _configured = true;
|
| + useCompactVMConfiguration();
|
| + filterStacks = true;
|
| +}
|
| +
|
| +/// Creates a new [PackageProvider] and [PackageGraph] 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.
|
| +///
|
| +/// [assets] may either be an [Iterable] or a [Map]. If it's an [Iterable],
|
| +/// each 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 should be a string defining the contents of that
|
| +/// asset.
|
| +///
|
| +/// [transformers] is a map from package names to the transformers for each
|
| +/// package.
|
| +void initGraph([assets,
|
| + Map<String, Iterable<Iterable<Transformer>>> transformers]) =>
|
| + initStaticGraph(assets, transformers: transformers);
|
| +
|
| +void initStaticGraph(assets, {Iterable<String> staticPackages,
|
| + Map<String, Iterable<Iterable<Transformer>>> transformers}) {
|
| + if (assets == null) assets = [];
|
| + if (staticPackages == null) staticPackages = [];
|
| + if (transformers == null) transformers = {};
|
| +
|
| + _provider = new MockProvider(assets,
|
| + staticPackages: staticPackages,
|
| + additionalPackages: transformers.keys);
|
| + _barback = new Barback(_provider);
|
| + // Add a dummy listener to the log so it doesn't print to stdout.
|
| + _barback.log.listen((_) {});
|
| + _nextBuildResult = 0;
|
| + _nextLog = 0;
|
| +
|
| + schedule(() => transformers.forEach(_barback.updateTransformers));
|
| +
|
| + // There should be one successful build after adding all the transformers but
|
| + // before adding any sources.
|
| + if (!transformers.isEmpty) buildShouldSucceed();
|
| +}
|
| +
|
| +/// Updates [assets] in the current [PackageProvider].
|
| +///
|
| +/// Each item in the list may either be an [AssetId] or a string that can be
|
| +/// parsed as one.
|
| +void updateSources(Iterable assets) {
|
| + assets = _parseAssets(assets);
|
| + schedule(() => _barback.updateSources(assets),
|
| + "updating ${assets.join(', ')}");
|
| +}
|
| +
|
| +/// Updates [assets] in the current [PackageProvider].
|
| +///
|
| +/// Each item in the list may either be an [AssetId] or a string that can be
|
| +/// parsed as one. Unlike [updateSources], this is not automatically scheduled
|
| +/// and will be run synchronously when called.
|
| +void updateSourcesSync(Iterable assets) =>
|
| + _barback.updateSources(_parseAssets(assets));
|
| +
|
| +/// Removes [assets] from the current [PackageProvider].
|
| +///
|
| +/// Each item in the list may either be an [AssetId] or a string that can be
|
| +/// parsed as one.
|
| +void removeSources(Iterable assets) {
|
| + assets = _parseAssets(assets);
|
| + schedule(() => _barback.removeSources(assets),
|
| + "removing ${assets.join(', ')}");
|
| +}
|
| +
|
| +/// Removes [assets] from the current [PackageProvider].
|
| +///
|
| +/// Each item in the list may either be an [AssetId] or a string that can be
|
| +/// parsed as one. Unlike [removeSources], this is not automatically scheduled
|
| +/// and will be run synchronously when called.
|
| +void removeSourcesSync(Iterable assets) =>
|
| + _barback.removeSources(_parseAssets(assets));
|
| +
|
| +/// Sets the transformers for [package] to [transformers].
|
| +void updateTransformers(String package, Iterable<Iterable> transformers) {
|
| + schedule(() => _barback.updateTransformers(package, transformers),
|
| + "updating transformers for $package");
|
| +}
|
| +
|
| +/// Parse a list of strings or [AssetId]s into a list of [AssetId]s.
|
| +List<AssetId> _parseAssets(Iterable assets) {
|
| + return assets.map((asset) {
|
| + if (asset is String) return new AssetId.parse(asset);
|
| + return asset;
|
| + }).toList();
|
| +}
|
| +
|
| +/// Schedules a change to 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");
|
| +}
|
| +
|
| +/// Schedules an error to be generated when loading the asset identified by
|
| +/// [name].
|
| +///
|
| +/// Does not update the asset in the graph. If [async] is true, the error is
|
| +/// thrown asynchronously.
|
| +void setAssetError(String name, {bool async: true}) {
|
| + schedule(() {
|
| + _provider._setAssetError(name, async);
|
| + }, "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
|
| +/// will not complete until [resumeProvider] is called.
|
| +void pauseProvider() {
|
| + schedule(() => _provider._pause(), "pause provider");
|
| +}
|
| +
|
| +/// Schedules an unpause of the provider after a call to [pauseProvider] and
|
| +/// allows all pending asset loads to finish.
|
| +void resumeProvider() {
|
| + schedule(() => _provider._resume(), "resume provider");
|
| +}
|
| +
|
| +/// Asserts that the current build step shouldn't have finished by this point in
|
| +/// the schedule.
|
| +///
|
| +/// 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() {
|
| + _futureShouldNotCompleteUntil(
|
| + _barback.results.elementAt(_nextBuildResult),
|
| + schedule(() => pumpEventQueue(), "build should not terminate"),
|
| + "build");
|
| +}
|
| +
|
| +/// Expects that the next [BuildResult] is a build success.
|
| +void buildShouldSucceed() {
|
| + expect(_getNextBuildResult("build should succeed").then((result) {
|
| + result.errors.forEach(currentSchedule.signalError);
|
| + expect(result.succeeded, isTrue);
|
| + }), completes);
|
| +}
|
| +
|
| +/// Expects that the next [BuildResult] emitted is a failure.
|
| +///
|
| +/// [matchers] is a list of matchers to match against the errors that caused the
|
| +/// build to fail. Every matcher is expected to match an error, but the order of
|
| +/// matchers is unimportant.
|
| +void buildShouldFail(List matchers) {
|
| + expect(_getNextBuildResult("build should fail").then((result) {
|
| + expect(result.succeeded, isFalse);
|
| + expect(result.errors.length, equals(matchers.length));
|
| + for (var matcher in matchers) {
|
| + expect(result.errors, contains(matcher));
|
| + }
|
| + }), completes);
|
| +}
|
| +
|
| +/// Expects that the nexted logged [LogEntry] matches [matcher] which may be
|
| +/// either a [Matcher] or a string to match a literal string.
|
| +void buildShouldLog(LogLevel level, matcher) {
|
| + expect(_getNextLog("build should log").then((log) {
|
| + expect(log.level, equals(level));
|
| + expect(log.message, matcher);
|
| + }), completes);
|
| +}
|
| +
|
| +Future<BuildResult> _getNextBuildResult(String description) {
|
| + var result = currentSchedule.wrapFuture(
|
| + _barback.results.elementAt(_nextBuildResult++));
|
| + return schedule(() => result, description);
|
| +}
|
| +
|
| +Future<LogEntry> _getNextLog(String description) {
|
| + var result = currentSchedule.wrapFuture(
|
| + _barback.log.elementAt(_nextLog++));
|
| + return schedule(() => result, description);
|
| +}
|
| +
|
| +/// Schedules an expectation that the graph will deliver an asset matching
|
| +/// [name] and [contents].
|
| +///
|
| +/// [contents] may be a [String] or a [Matcher] that matches a string. 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, [contents]) {
|
| + var id = new AssetId.parse(name);
|
| +
|
| + if (contents == null) {
|
| + contents = pathos.basenameWithoutExtension(id.path);
|
| + }
|
| +
|
| + schedule(() {
|
| + return _barback.getAssetById(id).then((asset) {
|
| + // TODO(rnystrom): Make an actual Matcher class for this.
|
| + expect(asset.id, equals(id));
|
| + expect(asset.readAsString(), completion(contents));
|
| + });
|
| + }, "get asset $name");
|
| +}
|
| +
|
| +/// Schedules an expectation 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 _barback.getAssetById(id).then((asset) {
|
| + fail("Should have thrown error but got $asset.");
|
| + }).catchError((error) {
|
| + expect(error, new isInstanceOf<AssetNotFoundException>());
|
| + expect(error.id, equals(id));
|
| + });
|
| + }, "get asset $name");
|
| +}
|
| +
|
| +/// Schedules an expectation that the graph will output all of the given
|
| +/// assets, and no others.
|
| +///
|
| +/// [assets] may be an iterable of asset id strings, in which case this asserts
|
| +/// that the graph outputs exactly the assets with those ids. It may also be a
|
| +/// map from asset id strings to asset contents, in which case the contents must
|
| +/// also match.
|
| +void expectAllAssets(assets) {
|
| + var expected;
|
| + var expectedString;
|
| + if (assets is Map) {
|
| + expected = mapMapKeys(assets, (key, _) => new AssetId.parse(key));
|
| + expectedString = expected.toString();
|
| + } else {
|
| + expected = assets.map((asset) => new AssetId.parse(asset));
|
| + expectedString = expected.join(', ');
|
| + }
|
| +
|
| + schedule(() {
|
| + return _barback.getAllAssets().then((actualAssets) {
|
| + var actualIds = actualAssets.map((asset) => asset.id).toSet();
|
| +
|
| + if (expected is Map) {
|
| + expected.forEach((id, contents) {
|
| + expect(actualIds, contains(id));
|
| + actualIds.remove(id);
|
| + expect(actualAssets[id].readAsString(), completion(equals(contents)));
|
| + });
|
| + } else {
|
| + for (var id in expected) {
|
| + expect(actualIds, contains(id));
|
| + actualIds.remove(id);
|
| + }
|
| + }
|
| +
|
| + expect(actualIds, isEmpty);
|
| + });
|
| + }, "get all assets, expecting $expectedString");
|
| +}
|
| +
|
| +/// Schedules an expectation that [Barback.getAllAssets] will return a [Future]
|
| +/// that completes to a error that matches [matcher].
|
| +///
|
| +/// If [match] is a [List], then it expects the completed error to be an
|
| +/// [AggregateException] whose errors match each matcher in the list. Otherwise,
|
| +/// [match] should be a single matcher that the error should match.
|
| +void expectAllAssetsShouldFail(Matcher matcher) {
|
| + schedule(() {
|
| + expect(_barback.getAllAssets(), throwsA(matcher));
|
| + }, "get all assets should fail");
|
| +}
|
| +
|
| +/// 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(
|
| + _barback.getAssetById(id),
|
| + pumpEventQueue(),
|
| + "asset $id");
|
| + }, "asset $id should not complete");
|
| +}
|
| +
|
| +/// Returns a matcher for an [AggregateException] containing errors that match
|
| +/// [matchers].
|
| +Matcher isAggregateException(Iterable<Matcher> errors) {
|
| + // Match the aggregate error itself.
|
| + var matchers = [
|
| + new isInstanceOf<AggregateException>(),
|
| + transform((error) => error.errors, hasLength(errors.length),
|
| + 'errors.length == ${errors.length}')
|
| + ];
|
| +
|
| + // Make sure its contained errors match the matchers.
|
| + for (var error in errors) {
|
| + matchers.add(transform((error) => error.errors, contains(error),
|
| + error.toString()));
|
| + }
|
| +
|
| + return allOf(matchers);
|
| +}
|
| +
|
| +/// Returns a matcher for an [AssetNotFoundException] with the given [id].
|
| +Matcher isAssetNotFoundException(String name) {
|
| + var id = new AssetId.parse(name);
|
| + return allOf(
|
| + new isInstanceOf<AssetNotFoundException>(),
|
| + predicate((error) => error.id == id, 'id == $name'));
|
| +}
|
| +
|
| +/// Returns a matcher for an [AssetCollisionException] with the given [id].
|
| +Matcher isAssetCollisionException(String name) {
|
| + var id = new AssetId.parse(name);
|
| + return allOf(
|
| + new isInstanceOf<AssetCollisionException>(),
|
| + predicate((error) => error.id == id, 'id == $name'));
|
| +}
|
| +
|
| +/// Returns a matcher for a [MissingInputException] with the given [id].
|
| +Matcher isMissingInputException(String name) {
|
| + var id = new AssetId.parse(name);
|
| + return allOf(
|
| + new isInstanceOf<MissingInputException>(),
|
| + predicate((error) => error.id == id, 'id == $name'));
|
| +}
|
| +
|
| +/// Returns a matcher for an [InvalidOutputException] with the given id.
|
| +Matcher isInvalidOutputException(String name) {
|
| + var id = new AssetId.parse(name);
|
| + return allOf(
|
| + new isInstanceOf<InvalidOutputException>(),
|
| + predicate((error) => error.id == id, 'id == $name'));
|
| +}
|
| +
|
| +/// Returns a matcher for an [AssetLoadException] with the given id and a
|
| +/// wrapped error that matches [error].
|
| +Matcher isAssetLoadException(String name, error) {
|
| + var id = new AssetId.parse(name);
|
| + return allOf(
|
| + new isInstanceOf<AssetLoadException>(),
|
| + transform((error) => error.id, equals(id), 'id'),
|
| + transform((error) => error.error, wrapMatcher(error), 'error'));
|
| +}
|
| +
|
| +/// Returns a matcher for a [TransformerException] with a wrapped error that
|
| +/// matches [error].
|
| +Matcher isTransformerException(error) {
|
| + return allOf(
|
| + new isInstanceOf<TransformerException>(),
|
| + transform((error) => error.error, wrapMatcher(error), 'error'));
|
| +}
|
| +
|
| +/// 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 == $name'));
|
| +}
|
| +
|
| +/// Returns a matcher that runs [transformation] on its input, then matches
|
| +/// the output against [matcher].
|
| +///
|
| +/// [description] should be a noun phrase that describes the relation of the
|
| +/// output of [transformation] to its input.
|
| +Matcher transform(transformation(value), matcher, String description) =>
|
| + new _TransformMatcher(transformation, wrapMatcher(matcher), description);
|
| +
|
| +class _TransformMatcher extends Matcher {
|
| + final Function _transformation;
|
| + final Matcher _matcher;
|
| + final String _description;
|
| +
|
| + _TransformMatcher(this._transformation, this._matcher, this._description);
|
| +
|
| + bool matches(item, Map matchState) =>
|
| + _matcher.matches(_transformation(item), matchState);
|
| +
|
| + Description describe(Description description) =>
|
| + description.add(_description).add(' ').addDescriptionOf(_matcher);
|
| +}
|
| +
|
| +/// 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 StaticPackageProvider {
|
| + Iterable<String> get packages =>
|
| + _assets.keys.toSet().difference(staticPackages);
|
| +
|
| + final Set<String> staticPackages;
|
| +
|
| + final Map<String, AssetSet> _assets;
|
| +
|
| + /// The set of assets for which [MockLoadException]s should be emitted if
|
| + /// they're loaded.
|
| + final _errors = new Set<AssetId>();
|
| +
|
| + /// The set of assets for which synchronous [MockLoadException]s should be
|
| + /// emitted if they're loaded.
|
| + final _syncErrors = new Set<AssetId>();
|
| +
|
| + /// The completer that [getAsset()] is waiting on to complete when paused.
|
| + ///
|
| + /// If `null` it will return the asset immediately.
|
| + Completer _pauseCompleter;
|
| +
|
| + /// Tells the provider to wait during [getAsset] until [complete()]
|
| + /// is called.
|
| + ///
|
| + /// Lets you test the asynchronous behavior of loading.
|
| + void _pause() {
|
| + _pauseCompleter = new Completer();
|
| + }
|
| +
|
| + void _resume() {
|
| + _pauseCompleter.complete();
|
| + _pauseCompleter = null;
|
| + }
|
| +
|
| + MockProvider(assets, {Iterable<String> staticPackages,
|
| + Iterable<String> additionalPackages})
|
| + : staticPackages = staticPackages == null ? new Set() :
|
| + staticPackages.toSet(),
|
| + _assets = _normalizeAssets(assets, additionalPackages);
|
| +
|
| + static Map<String, AssetSet> _normalizeAssets(assets,
|
| + Iterable<String> additionalPackages) {
|
| + var assetList;
|
| + if (assets is Map) {
|
| + assetList = assets.keys.map((asset) {
|
| + var id = new AssetId.parse(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);
|
| + });
|
| + }
|
| +
|
| + var assetMap = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
|
| + (package, assets) => new AssetSet.from(assets));
|
| +
|
| + // Make sure that packages that have transformers but no assets are
|
| + // considered by MockProvider to exist.
|
| + if (additionalPackages != null) {
|
| + for (var package in additionalPackages) {
|
| + assetMap.putIfAbsent(package, () => new AssetSet());
|
| + }
|
| + }
|
| +
|
| + // 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.
|
| + return assetMap.isEmpty ? {"app": new AssetSet()} : assetMap;
|
| + }
|
| +
|
| + void _modifyAsset(String name, String contents) {
|
| + var id = new AssetId.parse(name);
|
| + _errors.remove(id);
|
| + _syncErrors.remove(id);
|
| + (_assets[id.package][id] as _MockAsset).contents = contents;
|
| + }
|
| +
|
| + void _setAssetError(String name, bool async) {
|
| + (async ? _errors : _syncErrors).add(new AssetId.parse(name));
|
| + }
|
| +
|
| + Stream<AssetId> getAllAssetIds(String package) =>
|
| + new Stream.fromIterable(_assets[package].map((asset) => asset.id));
|
| +
|
| + 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 assets = _assets[id.package];
|
| + var asset;
|
| + if (assets != null) asset = assets[id];
|
| +
|
| + if (_syncErrors.contains(id)) throw new MockLoadException(id);
|
| + var hasError = _errors.contains(id);
|
| +
|
| + var future;
|
| + if (_pauseCompleter != null) {
|
| + future = _pauseCompleter.future;
|
| + } else {
|
| + future = new Future.value();
|
| + }
|
| +
|
| + return future.then((_) {
|
| + if (hasError) 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.";
|
| +}
|
| +
|
| +/// An implementation of [Asset] that never hits the file system.
|
| +class _MockAsset implements Asset {
|
| + final AssetId id;
|
| + String contents;
|
| +
|
| + _MockAsset(this.id, this.contents);
|
| +
|
| + Future<String> readAsString({Encoding encoding}) =>
|
| + new Future.value(contents);
|
| +
|
| + Stream<List<int>> read() => throw new UnimplementedError();
|
| +
|
| + String toString() => "MockAsset $id $contents";
|
| +}
|
|
|