blob: b0206370f09d44ac774bfbbc8dc8ce6d5d789eeb [file] [log] [blame]
// Copyright (c) 2014, 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.transformer.mock_aggregate;
import 'dart:async';
import 'package:barback/barback.dart';
import 'package:barback/src/utils.dart';
import 'package:scheduled_test/scheduled_test.dart';
/// The abstract base class for aggregate transformers used to test barback.
///
/// This adds the ability to pause and resume different components of the
/// transformers, and to tell whether they're running, when they start running,
/// and how many times they've run.
///
/// Transformers extending this should override [doClassifyPrimary] and
/// [doApply] rather than [classifyPrimary] and [apply], and they should use
/// [getInput] and [getPrimaryInputs] rather than [transform.getInput] and
/// [transform.primaryInputs].
abstract class MockAggregateTransformer extends AggregateTransformer {
/// The number of times the transformer has been applied.
///
/// This is scheduled. The Future will complete at the point in the schedule
/// that this is called.
Future<int> get numRuns => schedule(() => _numRuns);
var _numRuns = 0;
/// The number of currently running transforms.
int _runningTransforms = 0;
/// A completer for pausing the transformer before it finishes running
/// [apply].
Completer _apply;
/// Completers for pausing the transformer before it finishes running
/// [classifyPrimary].
final _classifyPrimary = new Map<AssetId, Completer>();
/// Completers for pausing the transformer before it finishes getting inputs
/// the [Transform].
final _getInput = new Map<AssetId, Completer>();
/// Completer for pausing the transformer before it accesses
/// [getPrimaryInputs].
Completer _primaryInputs;
/// A completer that completes once this transformer begins running.
///
/// Once this transformer finishes running, this is reset to a new completer,
/// so it can be used multiple times.
var _started = new Completer<Null>();
/// `true` if any transforms are currently running.
///
/// This is scheduled. The Future will complete at the point in the schedule
/// that this is called.
Future<bool> get isRunning => schedule(() => _runningTransforms > 0);
/// All elements of this set will be automatically consumed during [apply].
final consumePrimaries = new Set<String>();
/// Pauses the schedule until this transformer begins running.
void waitUntilStarted() {
schedule(() => _started.future, "wait until $this starts");
}
/// Causes the transformer to pause after running [apply] but before the
/// returned Future completes.
///
/// This can be resumed by calling [resumeApply]. This operation is scheduled.
void pauseApply() {
schedule(() {
_apply = new Completer();
}, "pause apply for $this");
}
/// Resumes the transformer's [apply] call after [pauseApply] was called.
///
/// This operation is scheduled.
void resumeApply() {
schedule(() {
_apply.complete();
_apply = null;
}, "resume apply for $this");
}
/// Causes the transformer to pause after running [classifyPrimary] on the
/// asset with the given [name], but before the returned Future completes.
///
/// This can be resumed by calling [resumeClassifyPrimary]. This operation is
/// scheduled.
void pauseClassifyPrimary(String name) {
schedule(() {
_classifyPrimary[new AssetId.parse(name)] = new Completer();
}, "pause classifyPrimary($name) for $this");
}
/// Resumes the transformer's [classifyPrimary] call on the asset with the
/// given [name] after [pauseClassifyPrimary] was called.
///
/// This operation is scheduled.
void resumeClassifyPrimary(String name) {
schedule(() {
_classifyPrimary.remove(new AssetId.parse(name)).complete();
}, "resume classifyPrimary($name) for $this");
}
/// Causes the transformer to pause while loading the secondary input with
/// the given [name].
///
/// This can be resumed by calling [resumeGetInput]. This operation is
/// scheduled.
void pauseGetInput(String name) {
schedule(() {
_getInput[new AssetId.parse(name)] = new Completer();
}, "pause getInput($name) for $this");
}
/// Resumes the transformer's loading of the input with the given [name] after
/// [pauseGetInput] was called.
///
/// This operation is scheduled.
void resumeGetInput(String name) {
schedule(() {
_getInput.remove(new AssetId.parse(name)).complete();
}, "resume getInput($name) for $this");
}
/// Causes the transformer to pause before accessing [getPrimaryInputs].
///
/// This can be resumed by calling [resumePrimaryInputs]. This operation is
/// scheduled.
void pausePrimaryInputs() {
schedule(() {
_primaryInputs = new Completer();
}, "pause primaryInputs for $this");
}
/// Resumes the transformer's invocation of [primaryInputs] after
/// [pausePrimaryInputs] was called.
///
/// This operation is scheduled.
void resumePrimaryInputs() {
schedule(() {
_primaryInputs.complete();
_primaryInputs = null;
}, "resume primaryInputs for $this");
}
/// Like [AggregateTransform.getInput], but respects [pauseGetInput].
///
/// This is intended for use by subclasses of [MockAggregateTransformer].
Future<Asset> getInput(AggregateTransform transform, AssetId id) async {
if (_getInput.containsKey(id)) await _getInput[id].future;
return await transform.getInput(id);
}
/// Like [AggregateTransform.primaryInputs], but respects
/// [pausePrimaryInputs].
///
/// This is intended for use by subclasses of [MockAggregateTransformer].
Stream<Asset> getPrimaryInputs(AggregateTransform transform) {
return futureStream(newFuture(() {
if (_primaryInputs != null) return _primaryInputs.future;
}).then((_) => transform.primaryInputs));
}
Future<String> classifyPrimary(AssetId id) async {
var result = await doClassifyPrimary(id);
if (_classifyPrimary.containsKey(id)) await _classifyPrimary[id].future;
return result;
}
Future apply(AggregateTransform transform) {
_numRuns++;
if (_runningTransforms == 0) _started.complete();
_runningTransforms++;
return newFuture(() => doApply(transform)).then((_) {
if (_apply != null) return _apply.future;
}).whenComplete(() {
for (var id in consumePrimaries) {
transform.consumePrimary(new AssetId.parse(id));
}
_runningTransforms--;
if (_runningTransforms == 0) _started = new Completer();
});
}
/// The wrapped version of [classifyPrimary] for subclasses to override.
///
/// This may return a `Future<String>` or, if it's entirely synchronous, a
/// `String`.
doClassifyPrimary(AssetId id);
/// The wrapped version of [doApply] for subclasses to override.
///
/// If this does asynchronous work, it should return a [Future] that completes
/// once it's finished.
doApply(AggregateTransform transform);
}