| Index: quiver/lib/testing/src/async/fake_async.dart
|
| diff --git a/quiver/lib/testing/src/async/fake_async.dart b/quiver/lib/testing/src/async/fake_async.dart
|
| deleted file mode 100644
|
| index eb14769718481cd38685405ce21f0468640ea5bb..0000000000000000000000000000000000000000
|
| --- a/quiver/lib/testing/src/async/fake_async.dart
|
| +++ /dev/null
|
| @@ -1,282 +0,0 @@
|
| -// Copyright 2014 Google Inc. All Rights Reserved.
|
| -//
|
| -// Licensed under the Apache License, Version 2.0 (the "License");
|
| -// you may not use this file except in compliance with the License.
|
| -// You may obtain a copy of the License at
|
| -//
|
| -// http://www.apache.org/licenses/LICENSE-2.0
|
| -//
|
| -// Unless required by applicable law or agreed to in writing, software
|
| -// distributed under the License is distributed on an "AS IS" BASIS,
|
| -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| -// See the License for the specific language governing permissions and
|
| -// limitations under the License.
|
| -
|
| -part of quiver.testing.async;
|
| -
|
| -/// A mechanism to make time-dependent units testable.
|
| -///
|
| -/// Test code can be passed as a callback to [run], which causes it to be run in
|
| -/// a [Zone] which fakes timer and microtask creation, such that they are run
|
| -/// during calls to [elapse] which simulates the asynchronous passage of time.
|
| -///
|
| -/// The synchronous passage of time (blocking or expensive calls) can also be
|
| -/// simulated using [elapseBlocking].
|
| -///
|
| -/// To allow the unit under test to tell time, it can receive a [Clock] as a
|
| -/// dependency, and default it to [const Clock()] in production, but then use
|
| -/// [clock] in test code.
|
| -///
|
| -/// Example:
|
| -///
|
| -/// test('testedFunc', () {
|
| -/// new FakeAsync().run((async) {
|
| -/// testedFunc(clock: async.getClock(initialTime));
|
| -/// async.elapse(duration);
|
| -/// expect(...)
|
| -/// });
|
| -/// });
|
| -abstract class FakeAsync {
|
| - factory FakeAsync() = _FakeAsync;
|
| -
|
| - FakeAsync._();
|
| -
|
| - /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and
|
| - /// [elapseBlocking].
|
| - ///
|
| - /// The returned clock starts at [initialTime], and calls to [elapse] and
|
| - /// [elapseBlocking] advance the clock, even if they occured before the call
|
| - /// to this method.
|
| - ///
|
| - /// The clock can be passed as a dependency to the unit under test.
|
| - Clock getClock(DateTime initialTime);
|
| -
|
| - /// Simulates the asynchronous passage of time.
|
| - ///
|
| - /// **This should only be called from within the zone used by [run].**
|
| - ///
|
| - /// If [duration] is negative, the returned future completes with an
|
| - /// [ArgumentError].
|
| - ///
|
| - /// If a previous call to [elapse] has not yet completed, throws a
|
| - /// [StateError].
|
| - ///
|
| - /// Any Timers created within the zone used by [run] which are to expire
|
| - /// at or before the new time after [duration] has elapsed are run.
|
| - /// The microtask queue is processed surrounding each timer. When a timer is
|
| - /// run, the [clock] will have been advanced by the timer's specified
|
| - /// duration. Calls to [elapseBlocking] from within these timers and
|
| - /// microtasks which cause the [clock] to elapse more than the specified
|
| - /// [duration], can cause more timers to expire and thus be called.
|
| - ///
|
| - /// Once all expired timers are processed, the [clock] is advanced (if
|
| - /// necessary) to the time this method was called + [duration].
|
| - void elapse(Duration duration);
|
| -
|
| - /// Simulates the synchronous passage of time, resulting from blocking or
|
| - /// expensive calls.
|
| - ///
|
| - /// Neither timers nor microtasks are run during this call. Upon return, the
|
| - /// [clock] will have been advanced by [duration].
|
| - ///
|
| - /// If [duration] is negative, throws an [ArgumentError].
|
| - void elapseBlocking(Duration duration);
|
| -
|
| - /// Runs [callback] in a [Zone] with fake timer and microtask scheduling.
|
| - ///
|
| - /// Uses
|
| - /// [ZoneSpecification.createTimer], [ZoneSpecification.createPeriodicTimer],
|
| - /// and [ZoneSpecification.scheduleMicrotask] to store callbacks for later
|
| - /// execution within the zone via calls to [elapse].
|
| - ///
|
| - /// [callback] is called with `this` as argument.
|
| - run(callback(FakeAsync self));
|
| -
|
| - /// Runs all remaining microtasks, including those scheduled as a result of
|
| - /// running them, until there are no more microtasks scheduled.
|
| - ///
|
| - /// Does not run timers.
|
| - void flushMicrotasks();
|
| -
|
| - /// Runs all timers until no timers remain (subject to [flushPeriodicTimers]
|
| - /// option), including those scheduled as a result of running them.
|
| - ///
|
| - /// [timeout] lets you set the maximum amount of time the flushing will take.
|
| - /// Throws a [StateError] if the [timeout] is exceeded. The default timeout
|
| - /// is 1 hour. [timeout] is relative to the elapsed time.
|
| - void flushTimers({Duration timeout: const Duration(hours: 1),
|
| - bool flushPeriodicTimers: true});
|
| -
|
| - /// The number of created periodic timers that have not been canceled.
|
| - int get periodicTimerCount;
|
| -
|
| - /// The number of pending non periodic timers that have not been canceled.
|
| - int get nonPeriodicTimerCount;
|
| -
|
| - /// The number of pending microtasks.
|
| - int get microtaskCount;
|
| -}
|
| -
|
| -class _FakeAsync extends FakeAsync {
|
| - Duration _elapsed = Duration.ZERO;
|
| - Duration _elapsingTo;
|
| - Queue<Function> _microtasks = new Queue();
|
| - Set<_FakeTimer> _timers = new Set<_FakeTimer>();
|
| -
|
| - _FakeAsync() : super._() {
|
| - _elapsed;
|
| - }
|
| -
|
| - @override
|
| - Clock getClock(DateTime initialTime) =>
|
| - new Clock(() => initialTime.add(_elapsed));
|
| -
|
| - @override
|
| - void elapse(Duration duration) {
|
| - if (duration.inMicroseconds < 0) {
|
| - throw new ArgumentError('Cannot call elapse with negative duration');
|
| - }
|
| - if (_elapsingTo != null) {
|
| - throw new StateError('Cannot elapse until previous elapse is complete.');
|
| - }
|
| - _elapsingTo = _elapsed + duration;
|
| - _drainTimersWhile((_FakeTimer next) => next._nextCall <= _elapsingTo);
|
| - _elapseTo(_elapsingTo);
|
| - _elapsingTo = null;
|
| - }
|
| -
|
| - @override
|
| - void elapseBlocking(Duration duration) {
|
| - if (duration.inMicroseconds < 0) {
|
| - throw new ArgumentError('Cannot call elapse with negative duration');
|
| - }
|
| - _elapsed += duration;
|
| - if (_elapsingTo != null && _elapsed > _elapsingTo) {
|
| - _elapsingTo = _elapsed;
|
| - }
|
| - }
|
| -
|
| - @override
|
| - void flushMicrotasks() {
|
| - _drainMicrotasks();
|
| - }
|
| -
|
| - @override
|
| - void flushTimers({Duration timeout: const Duration(hours: 1),
|
| - bool flushPeriodicTimers: true}) {
|
| - final absoluteTimeout = _elapsed + timeout;
|
| - _drainTimersWhile((_FakeTimer timer) {
|
| - if (timer._nextCall > absoluteTimeout) {
|
| - throw new StateError(
|
| - 'Exceeded timeout ${timeout} while flushing timers');
|
| - }
|
| - if (flushPeriodicTimers) {
|
| - return _timers.isNotEmpty;
|
| - } else {
|
| - // translation: keep draining while non-periodic timers exist
|
| - return _timers.any((_FakeTimer timer) => !timer._isPeriodic);
|
| - }
|
| - });
|
| - }
|
| -
|
| - @override
|
| - run(callback(FakeAsync self)) {
|
| - if (_zone == null) {
|
| - _zone = Zone.current.fork(specification: _zoneSpec);
|
| - }
|
| - return _zone.runGuarded(() => callback(this));
|
| - }
|
| - Zone _zone;
|
| -
|
| - @override
|
| - int get periodicTimerCount =>
|
| - _timers.where((_FakeTimer timer) => timer._isPeriodic).length;
|
| -
|
| - @override
|
| - int get nonPeriodicTimerCount =>
|
| - _timers.where((_FakeTimer timer) => !timer._isPeriodic).length;
|
| -
|
| - @override
|
| - int get microtaskCount => _microtasks.length;
|
| -
|
| - ZoneSpecification get _zoneSpec => new ZoneSpecification(
|
| - createTimer: (_, __, ___, Duration duration, Function callback) {
|
| - return _createTimer(duration, callback, false);
|
| - }, createPeriodicTimer: (_, __, ___, Duration duration, Function callback) {
|
| - return _createTimer(duration, callback, true);
|
| - }, scheduleMicrotask: (_, __, ___, Function microtask) {
|
| - _microtasks.add(microtask);
|
| - });
|
| -
|
| - _drainTimersWhile(bool predicate(_FakeTimer)) {
|
| - _drainMicrotasks();
|
| - _FakeTimer next;
|
| - while ((next = _getNextTimer()) != null && predicate(next)) {
|
| - _runTimer(next);
|
| - _drainMicrotasks();
|
| - }
|
| - }
|
| -
|
| - _elapseTo(Duration to) {
|
| - if (to > _elapsed) {
|
| - _elapsed = to;
|
| - }
|
| - }
|
| -
|
| - Timer _createTimer(Duration duration, Function callback, bool isPeriodic) {
|
| - var timer = new _FakeTimer._(duration, callback, isPeriodic, this);
|
| - _timers.add(timer);
|
| - return timer;
|
| - }
|
| -
|
| - _FakeTimer _getNextTimer() {
|
| - return min(_timers,
|
| - (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall));
|
| - }
|
| -
|
| - _runTimer(_FakeTimer timer) {
|
| - assert(timer.isActive);
|
| - _elapseTo(timer._nextCall);
|
| - if (timer._isPeriodic) {
|
| - timer._callback(timer);
|
| - timer._nextCall += timer._duration;
|
| - } else {
|
| - timer._callback();
|
| - _timers.remove(timer);
|
| - }
|
| - }
|
| -
|
| - _drainMicrotasks() {
|
| - while (_microtasks.isNotEmpty) {
|
| - _microtasks.removeFirst()();
|
| - }
|
| - }
|
| -
|
| - _hasTimer(_FakeTimer timer) => _timers.contains(timer);
|
| -
|
| - _cancelTimer(_FakeTimer timer) => _timers.remove(timer);
|
| -}
|
| -
|
| -class _FakeTimer implements Timer {
|
| - final Duration _duration;
|
| - final Function _callback;
|
| - final bool _isPeriodic;
|
| - final _FakeAsync _time;
|
| - Duration _nextCall;
|
| -
|
| - // TODO: In browser JavaScript, timers can only run every 4 milliseconds once
|
| - // sufficiently nested:
|
| - // http://www.w3.org/TR/html5/webappapis.html#timer-nesting-level
|
| - // Without some sort of delay this can lead to infinitely looping timers.
|
| - // What do the dart VM and dart2js timers do here?
|
| - static const _minDuration = Duration.ZERO;
|
| -
|
| - _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time)
|
| - : _duration = duration < _minDuration ? _minDuration : duration {
|
| - _nextCall = _time._elapsed + _duration;
|
| - }
|
| -
|
| - bool get isActive => _time._hasTimer(this);
|
| -
|
| - cancel() => _time._cancelTimer(this);
|
| -}
|
|
|