From de8e30c7139faf84ba198c1b1e69a602195a5ac1 Mon Sep 17 00:00:00 2001 From: Justin Fagnani Date: Wed, 14 Aug 2013 12:13:02 -0700 Subject: [PATCH 001/113] Initial commit --- pkgs/fake_async/LICENSE | 0 pkgs/fake_async/README.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 pkgs/fake_async/LICENSE create mode 100644 pkgs/fake_async/README.md diff --git a/pkgs/fake_async/LICENSE b/pkgs/fake_async/LICENSE new file mode 100644 index 000000000..e69de29bb diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md new file mode 100644 index 000000000..ab473ea6a --- /dev/null +++ b/pkgs/fake_async/README.md @@ -0,0 +1,3 @@ +This is a dummy commit generated when extracting `fake_async` from Quiver. + +See https://github.com/google/quiver-dart for the original history of this code. From 74959bcabb0331f97dad6bf6e2b108a2191d4d78 Mon Sep 17 00:00:00 2001 From: Justin Fagnani Date: Wed, 14 Aug 2013 20:57:47 -0700 Subject: [PATCH 002/113] Initial commit of project and iterables.dart --- pkgs/fake_async/LICENSE | 202 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) diff --git a/pkgs/fake_async/LICENSE b/pkgs/fake_async/LICENSE index e69de29bb..7a4a3ea24 100644 --- a/pkgs/fake_async/LICENSE +++ b/pkgs/fake_async/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file From 288c94bc3ac18fb6a2c0a57994c6ff9323d07244 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Wed, 12 Feb 2014 09:35:05 -0600 Subject: [PATCH 003/113] Implement basic synchronous version --- pkgs/fake_async/lib/fake_clock.dart | 115 ++++++++++++++ pkgs/fake_async/test/fake_clock_test.dart | 180 ++++++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 pkgs/fake_async/lib/fake_clock.dart create mode 100644 pkgs/fake_async/test/fake_clock_test.dart diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_clock.dart new file mode 100644 index 000000000..6698c23ba --- /dev/null +++ b/pkgs/fake_async/lib/fake_clock.dart @@ -0,0 +1,115 @@ +// Copyright 2013 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.time; + +class FakeClock extends Clock { + + DateTime _now; + + FakeClock({DateTime initialTime}) { + _now = initialTime == null ? new DateTime.now() : initialTime; + } + + DateTime now() => _now; + + static ZoneSpecification get _zoneSpec => new ZoneSpecification( + createTimer: _getZoneSpecTimerCallback(false), + createPeriodicTimer: _getZoneSpecTimerCallback(true) +// , +// handleUncaughtError: ( +// Zone self, +// ZoneDelegate parent, +// Zone zone, +// e, +// StackTrace stackTrace) { +// print('uncaught: $e'); +// return parent.handleUncaughtError(zone, e, stackTrace); +// } + ); + + static _getZoneSpecTimerCallback(bool periodic) => ( + Zone self, + ZoneDelegate parent, + Zone zone, + Duration duration, + Function callback) => self[#fakeClock]._createTimer(duration, callback, periodic); + + Zone get zone => Zone.current.fork(specification: _zoneSpec, zoneValues: {#fakeClock: this}); + + bool _isAdvancing = false; + + void advance(Duration duration) { + + if(duration.inMicroseconds < 0) { + throw new ArgumentError('Cannot call advance with negative Duration'); + } + if(_isAdvancing) { + throw new StateError('Cannot advance until previous advance is complete.'); + } + _isAdvancing = true; + var to = _now.add(duration); + _FakeTimer next; + while((next = getNextTimer(_now, to)) != null) { + _now = next._nextCall; + _runTimer(next); + } + _now = to; + _isAdvancing = false; + } + + _FakeTimer getNextTimer(DateTime from, DateTime to) { + return min(_timers.values.where((timer) { + return timer._nextCall.millisecondsSinceEpoch >= from.millisecondsSinceEpoch && + timer._nextCall.millisecondsSinceEpoch <= to.millisecondsSinceEpoch; + }), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); + } + + static var _nextTimerId = 1; + + Map _timers = {}; + _createTimer(Duration duration, Function callback, bool isPeriodic) { + var id = _nextTimerId++; + return _timers[id] = new _FakeTimer._(duration, callback, isPeriodic, this, id); + } + _runTimer(_FakeTimer timer) { + assert(timer.isActive); + if(timer._isPeriodic) { + timer._callback(timer); + timer._nextCall = timer._nextCall.add(timer._duration); + } else { + timer._callback(); + _timers.remove(timer._id); + } + } + _cancelTimer(_FakeTimer timer) => _timers.remove(timer._id); +} + +class _FakeTimer implements Timer { + + final int _id; + final Duration _duration; + final Function _callback; + final bool _isPeriodic; + final FakeClock _clock; + DateTime _nextCall; + + _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._clock, this._id) { + _nextCall = _clock.now().add(_duration); + } + + bool get isActive => _clock._timers.containsKey(_id); + + cancel() => _clock._cancelTimer(this); +} diff --git a/pkgs/fake_async/test/fake_clock_test.dart b/pkgs/fake_async/test/fake_clock_test.dart new file mode 100644 index 000000000..5c2e4becf --- /dev/null +++ b/pkgs/fake_async/test/fake_clock_test.dart @@ -0,0 +1,180 @@ +// Copyright 2013 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. + +library quiver.testing.time.fake_clock_test; + +import 'dart:async'; + +import 'package:quiver/testing/time.dart'; +import 'package:unittest/unittest.dart'; + +main() { + group('FakeClock', () { + + test('should set initial time', () { + var initialTime = new DateTime(2000); + var unit = new FakeClock(initialTime: initialTime); + expect(unit.now(), initialTime); + }); + + test('should default initial time to system clock time', () { + var systemNow = new DateTime.now(); + var unit = new FakeClock(); + expect( + unit.now().millisecondsSinceEpoch, + closeTo(systemNow.millisecondsSinceEpoch, 500)); + }); + + group('advance', () { + + test('should advance time', () { + var initialTime = new DateTime(2000); + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(initialTime: initialTime); + unit.advance(advanceBy); + expect(unit.now(), initialTime.add(advanceBy)); + }); + + test('should throw ArgumentError when called with a negative Duration', () { + var unit = new FakeClock(); + expect(() { + unit.advance(const Duration(days: -1)); + }, throwsA(new isInstanceOf())); + }); + + test('should throw when called before previous call is complete', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + unit.zone.run(() { + expect(() { + new Timer(advanceBy ~/ 2, () {unit.advance(advanceBy);}); + unit.advance(advanceBy); + }, throwsA(new isInstanceOf())); + }); + }); + + test('should call timers expiring before or at end time', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + int beforeCallCount = 0; + int atCallCount = 0; + unit.zone.run(() { + var before = new Timer(advanceBy ~/ 2, () {beforeCallCount++;}); + var at = new Timer(advanceBy, () {atCallCount++;}); + unit.advance(advanceBy); + }); + expect(beforeCallCount, 1); + expect(atCallCount, 1); + }); + + test('should call periodic timers once each time the duration elapses', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + int periodicCallCount = 0; + unit.zone.run(() { + var periodic = new Timer.periodic(const Duration(hours: 1), (_) {periodicCallCount++;}); + unit.advance(advanceBy); + }); + expect(periodicCallCount, 24); + }); + + test('should pass the periodic timer itself to callbacks', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + int periodicCallCount = 0; + Timer passedTimer; + Timer periodic; + unit.zone.run(() { + periodic = new Timer.periodic(advanceBy, (timer) {passedTimer = timer;}); + unit.advance(advanceBy); + }); + expect(passedTimer, periodic); + }); + + test('should not call timers expiring after end time', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + int timerCallCount = 0; + unit.zone.run(() { + var timer = new Timer(advanceBy * 2, () {timerCallCount++;}); + unit.advance(advanceBy); + }); + expect(timerCallCount, 0); + }); + + test('should not call canceled timers', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + int timerCallCount = 0; + unit.zone.run(() { + var timer = new Timer(advanceBy ~/ 2, () {timerCallCount++;}); + timer.cancel(); + unit.advance(advanceBy); + }); + expect(timerCallCount, 0); + }); + + test('should correctly implement isActive', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + Timer wasRun; + unit.zone.run(() { + wasRun = new Timer(advanceBy ~/ 2, () {}); + unit.advance(advanceBy); + }); + expect(wasRun.isActive, isFalse); + Timer periodicWasRun; + unit.zone.run(() { + periodicWasRun = new Timer.periodic(advanceBy ~/ 2, (_) {}); + unit.advance(advanceBy); + }); + expect(periodicWasRun.isActive, isTrue); + Timer wasCanceled; + unit.zone.run(() { + wasCanceled = new Timer(advanceBy * 2, () {}); + wasCanceled.cancel(); + }); + expect(wasCanceled.isActive, isFalse); + }); + }); + + test('should work with Future.delayed', () { + var advanceBy = const Duration(days: 1); + var unit = new FakeClock(); + Future delayed; + unit.zone.run(() { + delayed = new Future.delayed(advanceBy, () => 5); + unit.advance(advanceBy); + }); + return delayed.then((e) { + expect(e, 5); + }); + }); + + // TODO: Pausing and resuming the periodic Stream doesn't work since + // it uses `new Stopwatch()`. + test('should work with Stream.periodic', () { + var unit = new FakeClock(); + var events = []; + unit.zone.run(() { + var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); + var subscription = periodic.listen(events.add, cancelOnError: true); + unit.advance(const Duration(minutes: 3)); + subscription.cancel(); + }); + expect(events, [0, 1, 2]); + }); + + }); +} From b779436e0b823643c33c578e75c4bac2d0ba943c Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Thu, 20 Feb 2014 15:33:54 -0600 Subject: [PATCH 004/113] Initial implementation --- pkgs/fake_async/lib/fake_clock.dart | 225 +++++++++--- pkgs/fake_async/test/fake_clock_test.dart | 412 +++++++++++++++------- 2 files changed, 466 insertions(+), 171 deletions(-) diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_clock.dart index 6698c23ba..bd3c4786f 100644 --- a/pkgs/fake_async/lib/fake_clock.dart +++ b/pkgs/fake_async/lib/fake_clock.dart @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All Rights Reserved. +// 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. @@ -14,86 +14,207 @@ part of quiver.testing.time; -class FakeClock extends Clock { +abstract class FakeClock extends Clock { + + factory FakeClock({DateTime initialTime}) = _FakeClock; + + FakeClock._(); + + /// Simulate the asynchronous passage of [duration]. + /// + /// If [duration] is negative, the returned future completes with an + /// [ArgumentError]. + /// + /// If the future from the previous call to [advance] has not yet completed, + /// the returned future completes with a [StateError]. + /// + /// The advancement of this clock (and the completion of the returned future) + /// will not occur until some later turn of the event loop (after the + /// microtask queue has been drained). + /// + /// This should only be called when `Zone.current == zone` (or possibly + /// a fork of [zone]). + /// + /// Timers created within [zone] which are scheduled to expire at or before + /// the new time after the advancement will be run before the returned future + /// completes. When these timers are run, `now()` will return a time not + /// before the scheduled expiration time of the timer, potentially later if + /// there were calls to `advanceSync`. + /// + /// + Future advance(Duration duration); + + /// Simulate the synchronous passage of [duration]. + /// + /// If [duration] is negative, throws an ArgumentError. + void advanceSync(Duration duration); + + + /// The valid zone in which to call [advance]. This zone implements + /// [ZoneSpecification.createTimer] and + /// [ZoneSpecification.createPeriodicTimer] to create timers which will be + /// called during the completion of Futures returned from [advance]. + Zone zone; +} + +class _FakeClock extends FakeClock { DateTime _now; + DateTime _advancingTo; + Completer _advanceCompleter; - FakeClock({DateTime initialTime}) { + _FakeClock({DateTime initialTime}) : super._() { _now = initialTime == null ? new DateTime.now() : initialTime; } DateTime now() => _now; - static ZoneSpecification get _zoneSpec => new ZoneSpecification( - createTimer: _getZoneSpecTimerCallback(false), - createPeriodicTimer: _getZoneSpecTimerCallback(true) -// , -// handleUncaughtError: ( -// Zone self, -// ZoneDelegate parent, -// Zone zone, -// e, -// StackTrace stackTrace) { -// print('uncaught: $e'); -// return parent.handleUncaughtError(zone, e, stackTrace); -// } - ); - - static _getZoneSpecTimerCallback(bool periodic) => ( - Zone self, - ZoneDelegate parent, - Zone zone, - Duration duration, - Function callback) => self[#fakeClock]._createTimer(duration, callback, periodic); - - Zone get zone => Zone.current.fork(specification: _zoneSpec, zoneValues: {#fakeClock: this}); - - bool _isAdvancing = false; - - void advance(Duration duration) { - + Future advance(Duration duration) { if(duration.inMicroseconds < 0) { - throw new ArgumentError('Cannot call advance with negative Duration'); + return new Future.error(new ArgumentError('Cannot call advance with negative duration')); } - if(_isAdvancing) { - throw new StateError('Cannot advance until previous advance is complete.'); + if(_advancingTo != null) { + return new Future.error(new StateError('Cannot advance until previous advance is complete.')); } - _isAdvancing = true; - var to = _now.add(duration); - _FakeTimer next; - while((next = getNextTimer(_now, to)) != null) { - _now = next._nextCall; - _runTimer(next); - } - _now = to; - _isAdvancing = false; + _advancingTo = _now.add(duration); + _advanceCompleter = new Completer(); + return _advanceCompleter.future; } - _FakeTimer getNextTimer(DateTime from, DateTime to) { - return min(_timers.values.where((timer) { - return timer._nextCall.millisecondsSinceEpoch >= from.millisecondsSinceEpoch && - timer._nextCall.millisecondsSinceEpoch <= to.millisecondsSinceEpoch; - }), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); + void advanceSync(Duration duration) { + if(duration.inMicroseconds < 0) { + throw new ArgumentError('Cannot call advance with negative duration'); + } + _now = _now.add(duration); } - static var _nextTimerId = 1; + Zone get zone { + if(_zone == null) { + _zone = Zone.current.fork(specification: _zoneSpec); + } + return _zone; + } + Zone _zone; + + ZoneSpecification get _zoneSpec => new ZoneSpecification( + createTimer: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Duration duration, + Function callback) { + var bound = self.bindCallback(callback, runGuarded: true); + return _createTimer(duration, bound, false); + }, + createPeriodicTimer: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Duration duration, + Function callback) { + var bound = self.bindUnaryCallback(callback, runGuarded: true); + return _createTimer(duration, bound, true); + }, + scheduleMicrotask: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Function microtask) { + var bound = self.bindCallback(microtask, runGuarded: true); + parent.scheduleMicrotask(zone, bound); + }, + run: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Function f) { + var ret = parent.run(zone, f); + _scheduleTimer(self, parent, zone); + return ret; + }, + runUnary: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Function f, + arg) { + var ret = parent.runUnary(zone, f, arg); + _scheduleTimer(self, parent, zone); + return ret; + }, + runBinary: ( + Zone self, + ZoneDelegate parent, + Zone zone, + Function f, + arg1, + arg2) { + var ret = parent.runBinary(zone, f, arg1, arg2); + _scheduleTimer(self, parent, zone); + return ret; + }); + + _advanceTo(DateTime to) { + if(to.millisecondsSinceEpoch > _now.millisecondsSinceEpoch) { + _now = to; + } + } Map _timers = {}; + var _nextTimerId = 1; + bool _waitingForTimer = false; + _createTimer(Duration duration, Function callback, bool isPeriodic) { var id = _nextTimerId++; return _timers[id] = new _FakeTimer._(duration, callback, isPeriodic, this, id); } + + _scheduleTimer(Zone self, ZoneDelegate parent, Zone zone) { + + if(!_waitingForTimer && _advancingTo != null) { + var next = _getNextTimer(); + var completeTimer = next != null ? + self.bindCallback(() => _runTimer(next), runGuarded: true) : + () { + _advanceTo(_advancingTo); + _advancingTo = null; + _advanceCompleter.complete(); + _advanceCompleter = null; + }; + parent.createTimer(zone, Duration.ZERO, () { + completeTimer(); + _waitingForTimer = false; + }); + + _waitingForTimer = true; + } + } + + _FakeTimer _getNextTimer() { + return min(_timers.values.where((timer) => + timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || + (_advancingTo != null && + timer._nextCall.millisecondsSinceEpoch <= _advancingTo.millisecondsSinceEpoch) + ), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); + } + _runTimer(_FakeTimer timer) { assert(timer.isActive); + _advanceTo(timer._nextCall); if(timer._isPeriodic) { timer._callback(timer); - timer._nextCall = timer._nextCall.add(timer._duration); + // Move forward by at least 1 microsecond to avoid infinite loop. + // TODO: Move forward more (e.g. 15 - 20 milliseconds) ? + var duration = new Duration(microseconds: math.max(timer._duration.inMicroseconds, 1)); + timer._nextCall = timer._nextCall.add(duration); } else { timer._callback(); _timers.remove(timer._id); } } + _cancelTimer(_FakeTimer timer) => _timers.remove(timer._id); + } class _FakeTimer implements Timer { @@ -102,11 +223,11 @@ class _FakeTimer implements Timer { final Duration _duration; final Function _callback; final bool _isPeriodic; - final FakeClock _clock; + final _FakeClock _clock; DateTime _nextCall; _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._clock, this._id) { - _nextCall = _clock.now().add(_duration); + _nextCall = _clock.now().add(_duration.inMicroseconds.isNegative ? Duration.ZERO : _duration); } bool get isActive => _clock._timers.containsKey(_id); diff --git a/pkgs/fake_async/test/fake_clock_test.dart b/pkgs/fake_async/test/fake_clock_test.dart index 5c2e4becf..52ec571a5 100644 --- a/pkgs/fake_async/test/fake_clock_test.dart +++ b/pkgs/fake_async/test/fake_clock_test.dart @@ -1,4 +1,4 @@ -// Copyright 2013 Google Inc. All Rights Reserved. +// 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. @@ -22,159 +22,333 @@ import 'package:unittest/unittest.dart'; main() { group('FakeClock', () { + FakeClock unit; + DateTime initialTime; + Duration advanceBy; + + setUp(() { + initialTime = new DateTime(2000); + unit = new FakeClock(initialTime: initialTime); + advanceBy = const Duration(days: 1); + }); + test('should set initial time', () { - var initialTime = new DateTime(2000); - var unit = new FakeClock(initialTime: initialTime); expect(unit.now(), initialTime); }); test('should default initial time to system clock time', () { - var systemNow = new DateTime.now(); - var unit = new FakeClock(); expect( - unit.now().millisecondsSinceEpoch, - closeTo(systemNow.millisecondsSinceEpoch, 500)); + new FakeClock().now().millisecondsSinceEpoch, + closeTo(new DateTime.now().millisecondsSinceEpoch, 500)); }); - group('advance', () { + group('advanceSync', () { - test('should advance time', () { - var initialTime = new DateTime(2000); - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(initialTime: initialTime); - unit.advance(advanceBy); + test('should advance time synchronously', () { + unit.advanceSync(advanceBy); expect(unit.now(), initialTime.add(advanceBy)); }); - test('should throw ArgumentError when called with a negative Duration', () { - var unit = new FakeClock(); - expect(() { - unit.advance(const Duration(days: -1)); - }, throwsA(new isInstanceOf())); - }); + test('should throw ArgumentError when called with a negative duration', + () { + expect(() { + unit.advanceSync(const Duration(days: -1)); + }, throwsA(new isInstanceOf())); + }); - test('should throw when called before previous call is complete', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - unit.zone.run(() { - expect(() { - new Timer(advanceBy ~/ 2, () {unit.advance(advanceBy);}); - unit.advance(advanceBy); - }, throwsA(new isInstanceOf())); + }); + + group('advance', () { + + test('should advance time asynchronously', () { + Future advanced; + unit.zone.runGuarded(() { + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(unit.now(), initialTime.add(advanceBy)); }); }); - test('should call timers expiring before or at end time', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - int beforeCallCount = 0; - int atCallCount = 0; - unit.zone.run(() { - var before = new Timer(advanceBy ~/ 2, () {beforeCallCount++;}); - var at = new Timer(advanceBy, () {atCallCount++;}); + test('should throw ArgumentError when called with a negative duration', + () { + expect( + unit.advance(const Duration(days: -1)), + throwsA(new isInstanceOf())); + }); + + test('should throw when called before previous call is complete', () { + unit.zone.runGuarded(() { unit.advance(advanceBy); + expect(unit.advance(advanceBy), + throwsA(new isInstanceOf())); }); - expect(beforeCallCount, 1); - expect(atCallCount, 1); }); - test('should call periodic timers once each time the duration elapses', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - int periodicCallCount = 0; - unit.zone.run(() { - var periodic = new Timer.periodic(const Duration(hours: 1), (_) {periodicCallCount++;}); - unit.advance(advanceBy); + group('when creating timers', () { + + test('should call timers expiring before or at end time', () { + var beforeCallCount = 0; + var atCallCount = 0; + Future advanced; + unit.zone.runGuarded(() { + new Timer(advanceBy ~/ 2, () {beforeCallCount++;}); + new Timer(advanceBy, () {atCallCount++;}); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(beforeCallCount, 1); + expect(atCallCount, 1); + }); + }); - expect(periodicCallCount, 24); - }); - test('should pass the periodic timer itself to callbacks', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - int periodicCallCount = 0; - Timer passedTimer; - Timer periodic; - unit.zone.run(() { - periodic = new Timer.periodic(advanceBy, (timer) {passedTimer = timer;}); - unit.advance(advanceBy); + test('should call timers at their scheduled time', () { + DateTime calledAt; + var periodicCalledAt = []; + Future advanced; + unit.zone.runGuarded(() { + new Timer(advanceBy ~/ 2, () {calledAt = unit.now();}); + new Timer.periodic(advanceBy ~/ 2, (_) { + periodicCalledAt.add(unit.now());}); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(calledAt, initialTime.add(advanceBy ~/ 2)); + expect(periodicCalledAt, [initialTime.add(advanceBy ~/ 2), + initialTime.add(advanceBy)]); + }); + }); - expect(passedTimer, periodic); - }); - test('should not call timers expiring after end time', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - int timerCallCount = 0; - unit.zone.run(() { - var timer = new Timer(advanceBy * 2, () {timerCallCount++;}); - unit.advance(advanceBy); + test('should not call timers expiring after end time', () { + var timerCallCount = 0; + unit.zone.runGuarded(() { + new Timer(advanceBy * 2, () {timerCallCount++;}); + unit.advance(advanceBy); + }); + expect(timerCallCount, 0); }); - expect(timerCallCount, 0); - }); - test('should not call canceled timers', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - int timerCallCount = 0; - unit.zone.run(() { - var timer = new Timer(advanceBy ~/ 2, () {timerCallCount++;}); - timer.cancel(); - unit.advance(advanceBy); + test('should not call canceled timers', () { + int timerCallCount = 0; + Future advanced; + unit.zone.runGuarded(() { + var timer = new Timer(advanceBy ~/ 2, () {timerCallCount++;}); + timer.cancel(); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(timerCallCount, 0); + }); }); - expect(timerCallCount, 0); - }); - test('should correctly implement isActive', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - Timer wasRun; - unit.zone.run(() { - wasRun = new Timer(advanceBy ~/ 2, () {}); - unit.advance(advanceBy); + test('should call periodic timers each time the duration elapses', () { + var periodicCallCount = 0; + Future advanced; + unit.zone.runGuarded(() { + new Timer.periodic(advanceBy ~/ 10, (_) {periodicCallCount++;}); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(periodicCallCount, 10); + }); }); - expect(wasRun.isActive, isFalse); - Timer periodicWasRun; - unit.zone.run(() { - periodicWasRun = new Timer.periodic(advanceBy ~/ 2, (_) {}); - unit.advance(advanceBy); + + test('should pass the periodic timer itself to callbacks', () { + var periodicCallCount = 0; + Timer passedTimer; + Timer periodic; + Future advanced; + unit.zone.runGuarded(() { + periodic = new Timer.periodic(advanceBy, + (timer) {passedTimer = timer;}); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(periodic, passedTimer); + }); + }); - expect(periodicWasRun.isActive, isTrue); - Timer wasCanceled; - unit.zone.run(() { - wasCanceled = new Timer(advanceBy * 2, () {}); - wasCanceled.cancel(); + + test('should call microtasks before advancing time', () { + Future advanced; + DateTime calledAt; + unit.zone.runGuarded(() { + scheduleMicrotask((){ calledAt = unit.now(); }); + advanced = unit.advance(const Duration(minutes: 1)); + }); + return advanced.then((_) { + expect(calledAt, initialTime); + }); }); - expect(wasCanceled.isActive, isFalse); - }); - }); - test('should work with Future.delayed', () { - var advanceBy = const Duration(days: 1); - var unit = new FakeClock(); - Future delayed; - unit.zone.run(() { - delayed = new Future.delayed(advanceBy, () => 5); - unit.advance(advanceBy); - }); - return delayed.then((e) { - expect(e, 5); - }); - }); + test('should add event before advancing time', () { + var events = []; + var controller = new StreamController(); + Future advanced; + DateTime heardAt; + unit.zone.runGuarded(() { + controller.stream.first.then((_) { heardAt = unit.now(); }); + controller.add(null); + advanced = unit.advance(const Duration(minutes: 1)); + }); + return Future.wait([controller.close(), advanced]).then((_) { + expect(heardAt, initialTime); + }); + }); + + test('should increase negative duration timers to zero duration', () { + var negativeDuration = const Duration(days: -1); + Future advanced; + DateTime calledAt; + unit.zone.runGuarded(() { + new Timer(negativeDuration, () { calledAt = unit.now(); }); + advanced = unit.advance(const Duration(minutes: 1)); + }); + return advanced.then((_) { + expect(calledAt, initialTime); + }); + }); + + test('should not be additive with advanceSync', () { + Future advanced; + unit.zone.runGuarded(() { + advanced = unit.advance(advanceBy); + unit.advanceSync(advanceBy * 2); + }); + return advanced.then((_) { + expect(unit.now(), initialTime.add(advanceBy * 2)); + }); + }); + + group('isActive', () { + + test('should be false after timer is run', () { + Timer timer; + Future advanced; + unit.zone.runGuarded(() { + timer = new Timer(advanceBy ~/ 2, () {}); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(timer.isActive, isFalse); + }); + }); + + test('should be true after periodic timer is run', () { + Timer timer; + Future advanced; + unit.zone.runGuarded(() { + timer = new Timer.periodic(advanceBy ~/ 2, (_) {}); + advanced = unit.advance(advanceBy); + }); + return advanced.then((_) { + expect(timer.isActive, isTrue); + }); + }); + + test('should be false after timer is canceled', () { + Timer timer; + unit.zone.runGuarded(() { + timer = new Timer(advanceBy ~/ 2, () {}); + timer.cancel(); + }); + expect(timer.isActive, isFalse); + }); + + }); + + test('should work with new Future()', () { + var callCount = 0; + Future advanced; + unit.zone.runGuarded(() { + new Future(() => callCount++); + advanced = unit.advance(Duration.ZERO); + }); + return advanced.then((_) { + expect(callCount, 1); + }); + + }); + + test('should work with Future.delayed', () { + Future delayed; + unit.zone.runGuarded(() { + delayed = new Future.delayed(advanceBy, () => 5); + unit.advance(advanceBy); + }); + return delayed.then((e) { + expect(e, 5); + }); + }); + + test('should work with Future.timeout', () { + var completer = new Completer(); + unit.zone.runGuarded(() { + var timed = completer.future.timeout(advanceBy ~/ 2); + new Timer(advanceBy, completer.complete); + unit.advance(advanceBy); + expect(timed, throwsA(new isInstanceOf())); + }); + }); + + // TODO: Pausing and resuming the periodic Stream doesn't work since + // it uses `new Stopwatch()`. + test('should work with Stream.periodic', () { + var events = []; + Future advanced; + StreamSubscription subscription; + unit.zone.runGuarded(() { + var periodic = new Stream.periodic(const Duration(minutes: 1), + (i) => i); + subscription = periodic.listen(events.add, cancelOnError: true); + advanced = unit.advance(const Duration(minutes: 3)); + }); + return advanced.then((_) { + subscription.cancel(); + expect(events, [0, 1, 2]); + }); + + }); + + // TODO: Pausing and resuming the periodic Stream doesn't work since + // it uses `new Stopwatch()`. + test('should work with Stream.timeout', () { + var events = []; + var errors = []; + var controller = new StreamController(); + StreamSubscription subscription; + Future advanced; + unit.zone.runGuarded(() { + var timed = controller.stream.timeout(const Duration(minutes: 2)); + subscription = timed.listen((event) { + events.add(event); + }, onError: errors.add, cancelOnError: true); + controller.add(0); + advanced = unit.advance(const Duration(minutes: 1)); + }); + return advanced.then((_) { + expect(events, [0]); + Future advanced; + unit.zone.runGuarded(() { + advanced = unit.advance(const Duration(minutes: 1)); + }); + return advanced.then((_) { + subscription.cancel(); + expect(errors, hasLength(1)); + expect(errors.first, new isInstanceOf()); + return controller.close(); + }); + + }); + + }); - // TODO: Pausing and resuming the periodic Stream doesn't work since - // it uses `new Stopwatch()`. - test('should work with Stream.periodic', () { - var unit = new FakeClock(); - var events = []; - unit.zone.run(() { - var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); - var subscription = periodic.listen(events.add, cancelOnError: true); - unit.advance(const Duration(minutes: 3)); - subscription.cancel(); }); - expect(events, [0, 1, 2]); + }); }); + } From a42b722eb72b4d843ff887473bcb45263c0211f9 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Thu, 20 Feb 2014 16:19:30 -0600 Subject: [PATCH 005/113] cleanup --- pkgs/fake_async/lib/fake_clock.dart | 51 +++++++++++++---------- pkgs/fake_async/test/fake_clock_test.dart | 12 ++---- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_clock.dart index bd3c4786f..2057c6433 100644 --- a/pkgs/fake_async/lib/fake_clock.dart +++ b/pkgs/fake_async/lib/fake_clock.dart @@ -14,13 +14,18 @@ part of quiver.testing.time; +/// A fake clock which is manually advanced either asynchronously ([advance]) +/// or synchronously ([advanceSync]), to simulate the passage of time. abstract class FakeClock extends Clock { factory FakeClock({DateTime initialTime}) = _FakeClock; FakeClock._(); - /// Simulate the asynchronous passage of [duration]. + /// Simulate the asynchronous advancement of this clock by [duration]. + /// + /// Important: This should only be called when `Zone.current == zone` + /// (or possibly a fork of [zone]). /// /// If [duration] is negative, the returned future completes with an /// [ArgumentError]. @@ -32,25 +37,20 @@ abstract class FakeClock extends Clock { /// will not occur until some later turn of the event loop (after the /// microtask queue has been drained). /// - /// This should only be called when `Zone.current == zone` (or possibly - /// a fork of [zone]). - /// /// Timers created within [zone] which are scheduled to expire at or before /// the new time after the advancement will be run before the returned future - /// completes. When these timers are run, `now()` will return a time not - /// before the scheduled expiration time of the timer, potentially later if - /// there were calls to `advanceSync`. - /// - /// + /// completes. When these timers are run, `now()` will have been advanced + /// by the timer's specified duration, potentially more if there were calls + /// to [advanceSync] as well. Future advance(Duration duration); - /// Simulate the synchronous passage of [duration]. + /// Simulate the synchronous advancement of this clock by [duration]. /// /// If [duration] is negative, throws an ArgumentError. void advanceSync(Duration duration); - /// The valid zone in which to call [advance]. This zone implements + /// The valid zone in which to call [advance]. Implements /// [ZoneSpecification.createTimer] and /// [ZoneSpecification.createPeriodicTimer] to create timers which will be /// called during the completion of Futures returned from [advance]. @@ -71,10 +71,12 @@ class _FakeClock extends FakeClock { Future advance(Duration duration) { if(duration.inMicroseconds < 0) { - return new Future.error(new ArgumentError('Cannot call advance with negative duration')); + return new Future.error( + new ArgumentError('Cannot call advance with negative duration')); } if(_advancingTo != null) { - return new Future.error(new StateError('Cannot advance until previous advance is complete.')); + return new Future.error( + new StateError('Cannot advance until previous advance is complete.')); } _advancingTo = _now.add(duration); _advanceCompleter = new Completer(); @@ -166,7 +168,8 @@ class _FakeClock extends FakeClock { _createTimer(Duration duration, Function callback, bool isPeriodic) { var id = _nextTimerId++; - return _timers[id] = new _FakeTimer._(duration, callback, isPeriodic, this, id); + return _timers[id] = + new _FakeTimer._(duration, callback, isPeriodic, this, id); } _scheduleTimer(Zone self, ZoneDelegate parent, Zone zone) { @@ -194,7 +197,8 @@ class _FakeClock extends FakeClock { return min(_timers.values.where((timer) => timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || (_advancingTo != null && - timer._nextCall.millisecondsSinceEpoch <= _advancingTo.millisecondsSinceEpoch) + timer._nextCall.millisecondsSinceEpoch <= + _advancingTo.millisecondsSinceEpoch) ), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } @@ -203,10 +207,7 @@ class _FakeClock extends FakeClock { _advanceTo(timer._nextCall); if(timer._isPeriodic) { timer._callback(timer); - // Move forward by at least 1 microsecond to avoid infinite loop. - // TODO: Move forward more (e.g. 15 - 20 milliseconds) ? - var duration = new Duration(microseconds: math.max(timer._duration.inMicroseconds, 1)); - timer._nextCall = timer._nextCall.add(duration); + timer._nextCall = timer._nextCall.add(timer._duration); } else { timer._callback(); _timers.remove(timer._id); @@ -226,8 +227,16 @@ class _FakeTimer implements Timer { final _FakeClock _clock; DateTime _nextCall; - _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._clock, this._id) { - _nextCall = _clock.now().add(_duration.inMicroseconds.isNegative ? Duration.ZERO : _duration); + _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._clock, + this._id) { + // TODO: Figure out how to handle nested or periodic timers with zero + // duration without getting into infinite loop. + // In browser JavaScript, timers can only run every 4 milliseconds once + // sufficiently nested: + // http://www.w3.org/TR/html5/webappapis.html#timer-nesting-level + // What do the dart VM and dart2js timers do here? + _nextCall = _clock.now().add(_duration.inMicroseconds.isNegative ? + Duration.ZERO : _duration); } bool get isActive => _clock._timers.containsKey(_id); diff --git a/pkgs/fake_async/test/fake_clock_test.dart b/pkgs/fake_async/test/fake_clock_test.dart index 52ec571a5..10f0067f6 100644 --- a/pkgs/fake_async/test/fake_clock_test.dart +++ b/pkgs/fake_async/test/fake_clock_test.dart @@ -100,7 +100,6 @@ main() { expect(beforeCallCount, 1); expect(atCallCount, 1); }); - }); test('should call timers at their scheduled time', () { @@ -118,7 +117,6 @@ main() { expect(periodicCalledAt, [initialTime.add(advanceBy ~/ 2), initialTime.add(advanceBy)]); }); - }); test('should not call timers expiring after end time', () { @@ -168,7 +166,6 @@ main() { return advanced.then((_) { expect(periodic, passedTimer); }); - }); test('should call microtasks before advancing time', () { @@ -269,7 +266,6 @@ main() { return advanced.then((_) { expect(callCount, 1); }); - }); test('should work with Future.delayed', () { @@ -288,12 +284,12 @@ main() { unit.zone.runGuarded(() { var timed = completer.future.timeout(advanceBy ~/ 2); new Timer(advanceBy, completer.complete); - unit.advance(advanceBy); - expect(timed, throwsA(new isInstanceOf())); + var advanced = unit.advance(advanceBy); + expect(Future.wait([advanced, timed]), throwsA(new isInstanceOf())); }); }); - // TODO: Pausing and resuming the periodic Stream doesn't work since + // TODO: Pausing and resuming the timeout Stream doesn't work since // it uses `new Stopwatch()`. test('should work with Stream.periodic', () { var events = []; @@ -312,8 +308,6 @@ main() { }); - // TODO: Pausing and resuming the periodic Stream doesn't work since - // it uses `new Stopwatch()`. test('should work with Stream.timeout', () { var events = []; var errors = []; From fab113bb9cead517ef37cf8c7b53e36dfbdb46bb Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Thu, 20 Feb 2014 16:22:26 -0600 Subject: [PATCH 006/113] replace 'if(' with 'if (' --- pkgs/fake_async/lib/fake_clock.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_clock.dart index 2057c6433..0d7a8999f 100644 --- a/pkgs/fake_async/lib/fake_clock.dart +++ b/pkgs/fake_async/lib/fake_clock.dart @@ -70,11 +70,11 @@ class _FakeClock extends FakeClock { DateTime now() => _now; Future advance(Duration duration) { - if(duration.inMicroseconds < 0) { + if (duration.inMicroseconds < 0) { return new Future.error( new ArgumentError('Cannot call advance with negative duration')); } - if(_advancingTo != null) { + if (_advancingTo != null) { return new Future.error( new StateError('Cannot advance until previous advance is complete.')); } @@ -84,14 +84,14 @@ class _FakeClock extends FakeClock { } void advanceSync(Duration duration) { - if(duration.inMicroseconds < 0) { + if (duration.inMicroseconds < 0) { throw new ArgumentError('Cannot call advance with negative duration'); } _now = _now.add(duration); } Zone get zone { - if(_zone == null) { + if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); } return _zone; @@ -157,7 +157,7 @@ class _FakeClock extends FakeClock { }); _advanceTo(DateTime to) { - if(to.millisecondsSinceEpoch > _now.millisecondsSinceEpoch) { + if (to.millisecondsSinceEpoch > _now.millisecondsSinceEpoch) { _now = to; } } @@ -174,7 +174,7 @@ class _FakeClock extends FakeClock { _scheduleTimer(Zone self, ZoneDelegate parent, Zone zone) { - if(!_waitingForTimer && _advancingTo != null) { + if (!_waitingForTimer && _advancingTo != null) { var next = _getNextTimer(); var completeTimer = next != null ? self.bindCallback(() => _runTimer(next), runGuarded: true) : @@ -205,7 +205,7 @@ class _FakeClock extends FakeClock { _runTimer(_FakeTimer timer) { assert(timer.isActive); _advanceTo(timer._nextCall); - if(timer._isPeriodic) { + if (timer._isPeriodic) { timer._callback(timer); timer._nextCall = timer._nextCall.add(timer._duration); } else { From fe202eb3ed89f2a02782bd74ea057b8b3c03e3a3 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Thu, 27 Mar 2014 15:56:45 -0500 Subject: [PATCH 007/113] Remove dependency on Clock, rename FakeClock to FakeTime --- pkgs/fake_async/lib/fake_clock.dart | 46 +++++++++++------------ pkgs/fake_async/test/fake_clock_test.dart | 12 +++--- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_clock.dart index 0d7a8999f..083eff06a 100644 --- a/pkgs/fake_async/lib/fake_clock.dart +++ b/pkgs/fake_async/lib/fake_clock.dart @@ -14,18 +14,17 @@ part of quiver.testing.time; -/// A fake clock which is manually advanced either asynchronously ([advance]) +/// A fake time which is manually advanced either asynchronously ([advance]) /// or synchronously ([advanceSync]), to simulate the passage of time. -abstract class FakeClock extends Clock { +abstract class FakeTime { - factory FakeClock({DateTime initialTime}) = _FakeClock; + factory FakeTime({DateTime initialTime}) = _FakeTime; - FakeClock._(); + FakeTime._(); - /// Simulate the asynchronous advancement of this clock by [duration]. + /// Simulate the asynchronous advancement of this time by [duration]. /// - /// Important: This should only be called when `Zone.current == zone` - /// (or possibly a fork of [zone]). + /// Important: This should only be called from inside the [zone]. /// /// If [duration] is negative, the returned future completes with an /// [ArgumentError]. @@ -33,18 +32,19 @@ abstract class FakeClock extends Clock { /// If the future from the previous call to [advance] has not yet completed, /// the returned future completes with a [StateError]. /// - /// The advancement of this clock (and the completion of the returned future) - /// will not occur until some later turn of the event loop (after the - /// microtask queue has been drained). + /// Any Timers created within [zone] which are scheduled to expire at or before + /// the new time after the advancement, are run, each in their own event + /// loop as normal, except that there is no actual delay before each timer run. + /// When a timer is run, `now()` will have been advanced by the timer's + /// specified duration, potentially more if there were calls to [advanceSync] + /// as well. /// - /// Timers created within [zone] which are scheduled to expire at or before - /// the new time after the advancement will be run before the returned future - /// completes. When these timers are run, `now()` will have been advanced - /// by the timer's specified duration, potentially more if there were calls - /// to [advanceSync] as well. + /// When there are no more timers to run, or the next timer is beyond the + /// time advancement frame, `now()` is updated to return has advanced by + /// [duration], the returned Future is completed. Future advance(Duration duration); - /// Simulate the synchronous advancement of this clock by [duration]. + /// Simulate the synchronous advancement of this time by [duration]. /// /// If [duration] is negative, throws an ArgumentError. void advanceSync(Duration duration); @@ -57,13 +57,13 @@ abstract class FakeClock extends Clock { Zone zone; } -class _FakeClock extends FakeClock { +class _FakeTime extends FakeTime { DateTime _now; DateTime _advancingTo; Completer _advanceCompleter; - _FakeClock({DateTime initialTime}) : super._() { + _FakeTime({DateTime initialTime}) : super._() { _now = initialTime == null ? new DateTime.now() : initialTime; } @@ -224,10 +224,10 @@ class _FakeTimer implements Timer { final Duration _duration; final Function _callback; final bool _isPeriodic; - final _FakeClock _clock; + final _FakeTime _time; DateTime _nextCall; - _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._clock, + _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._time, this._id) { // TODO: Figure out how to handle nested or periodic timers with zero // duration without getting into infinite loop. @@ -235,11 +235,11 @@ class _FakeTimer implements Timer { // sufficiently nested: // http://www.w3.org/TR/html5/webappapis.html#timer-nesting-level // What do the dart VM and dart2js timers do here? - _nextCall = _clock.now().add(_duration.inMicroseconds.isNegative ? + _nextCall = _time.now().add(_duration.inMicroseconds.isNegative ? Duration.ZERO : _duration); } - bool get isActive => _clock._timers.containsKey(_id); + bool get isActive => _time._timers.containsKey(_id); - cancel() => _clock._cancelTimer(this); + cancel() => _time._cancelTimer(this); } diff --git a/pkgs/fake_async/test/fake_clock_test.dart b/pkgs/fake_async/test/fake_clock_test.dart index 10f0067f6..6798034fb 100644 --- a/pkgs/fake_async/test/fake_clock_test.dart +++ b/pkgs/fake_async/test/fake_clock_test.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -library quiver.testing.time.fake_clock_test; +library quiver.testing.time.fake_time_test; import 'dart:async'; @@ -20,15 +20,15 @@ import 'package:quiver/testing/time.dart'; import 'package:unittest/unittest.dart'; main() { - group('FakeClock', () { + group('FakeTime', () { - FakeClock unit; + FakeTime unit; DateTime initialTime; Duration advanceBy; setUp(() { initialTime = new DateTime(2000); - unit = new FakeClock(initialTime: initialTime); + unit = new FakeTime(initialTime: initialTime); advanceBy = const Duration(days: 1); }); @@ -36,9 +36,9 @@ main() { expect(unit.now(), initialTime); }); - test('should default initial time to system clock time', () { + test('should default initial time to system time', () { expect( - new FakeClock().now().millisecondsSinceEpoch, + new FakeTime().now().millisecondsSinceEpoch, closeTo(new DateTime.now().millisecondsSinceEpoch, 500)); }); From c9ce5371d4564a0fe482460a50eafa8211095c7d Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Fri, 28 Mar 2014 12:39:11 -0500 Subject: [PATCH 008/113] Replace `zone` with simple `run` method --- pkgs/fake_async/lib/fake_clock.dart | 53 ++++--- pkgs/fake_async/test/fake_clock_test.dart | 160 +++++++++------------- 2 files changed, 98 insertions(+), 115 deletions(-) diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_clock.dart index 083eff06a..a6d08b0cd 100644 --- a/pkgs/fake_async/lib/fake_clock.dart +++ b/pkgs/fake_async/lib/fake_clock.dart @@ -14,17 +14,35 @@ part of quiver.testing.time; -/// A fake time which is manually advanced either asynchronously ([advance]) -/// or synchronously ([advanceSync]), to simulate the passage of time. +/// A mechanism to make time-dependent units testable. +/// +/// To use this, test code must be run within a [run] callback. Any [Timer]s +/// created there will be fake. Calling [advance] will then manually advance +/// the time returned by [now], calling any fake timers as they expire. +/// +/// Time can also be advanced synchronously ([advanceSync]) to simulate +/// expensive or blocking calls, in this case timers are not called. +/// +/// The unit under test can take a [TimeFunction] as a dependency, and +/// default it to something like `() => new DateTime.now()` in production, but +/// then have tests pass a closure of [FakeTime.now]. Or for a higher-level +/// interface, see [Clock], which takes a [TimeFunction] as a dependency. abstract class FakeTime { + /// [initialTime] will be the time returned by [now] before any calls to + /// [advance] or [advanceSync]. factory FakeTime({DateTime initialTime}) = _FakeTime; FakeTime._(); - /// Simulate the asynchronous advancement of this time by [duration]. + /// Returns the current (fake) time. A time-dependent unit can receive a + /// [TimeFunction] as a dependency. A unit can take one of these as a dependency, and either Pass a closure of this method to the + /// unit under test so that it received a + DateTime now() => _now; + + /// Simulate the asynchronous advancement of time by [duration]. /// - /// Important: This should only be called from inside the [zone]. + /// Important: This should only be called from inside a [run] callback. /// /// If [duration] is negative, the returned future completes with an /// [ArgumentError]. @@ -32,29 +50,28 @@ abstract class FakeTime { /// If the future from the previous call to [advance] has not yet completed, /// the returned future completes with a [StateError]. /// - /// Any Timers created within [zone] which are scheduled to expire at or before - /// the new time after the advancement, are run, each in their own event - /// loop as normal, except that there is no actual delay before each timer run. - /// When a timer is run, `now()` will have been advanced by the timer's - /// specified duration, potentially more if there were calls to [advanceSync] - /// as well. + /// Any Timers created within a [run] callback which are scheduled to expire + /// at or before the new time after the advancement, are run, each in their + /// own event loop frame as normal, except that there is no actual delay + /// before each timer run. When a timer is run, `now()` will have been + /// advanced by the timer's specified duration, potentially more if there were + /// calls to [advanceSync] as well. /// /// When there are no more timers to run, or the next timer is beyond the - /// time advancement frame, `now()` is updated to return has advanced by - /// [duration], the returned Future is completed. + /// end time (time when called + [duration]), `now()` is advanced to the end + /// time, and the returned Future is completed. Future advance(Duration duration); /// Simulate the synchronous advancement of this time by [duration]. /// - /// If [duration] is negative, throws an ArgumentError. + /// If [duration] is negative, throws an [ArgumentError]. void advanceSync(Duration duration); - - /// The valid zone in which to call [advance]. Implements + /// Runs [callback] in a [Zone] which implements /// [ZoneSpecification.createTimer] and /// [ZoneSpecification.createPeriodicTimer] to create timers which will be /// called during the completion of Futures returned from [advance]. - Zone zone; + run(callback()); } class _FakeTime extends FakeTime { @@ -90,11 +107,11 @@ class _FakeTime extends FakeTime { _now = _now.add(duration); } - Zone get zone { + run(callback()) { if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); } - return _zone; + return _zone.runGuarded(callback); } Zone _zone; diff --git a/pkgs/fake_async/test/fake_clock_test.dart b/pkgs/fake_async/test/fake_clock_test.dart index 6798034fb..d62faa6a0 100644 --- a/pkgs/fake_async/test/fake_clock_test.dart +++ b/pkgs/fake_async/test/fake_clock_test.dart @@ -60,15 +60,10 @@ main() { group('advance', () { - test('should advance time asynchronously', () { - Future advanced; - unit.zone.runGuarded(() { - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { - expect(unit.now(), initialTime.add(advanceBy)); - }); - }); + test('should advance time asynchronously', () => + unit.run(() => unit.advance(advanceBy)).then((_) { + expect(unit.now(), initialTime.add(advanceBy)); + })); test('should throw ArgumentError when called with a negative duration', () { @@ -78,7 +73,7 @@ main() { }); test('should throw when called before previous call is complete', () { - unit.zone.runGuarded(() { + unit.run(() { unit.advance(advanceBy); expect(unit.advance(advanceBy), throwsA(new isInstanceOf())); @@ -90,13 +85,11 @@ main() { test('should call timers expiring before or at end time', () { var beforeCallCount = 0; var atCallCount = 0; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { new Timer(advanceBy ~/ 2, () {beforeCallCount++;}); new Timer(advanceBy, () {atCallCount++;}); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { + return unit.advance(advanceBy); + }).then((_) { expect(beforeCallCount, 1); expect(atCallCount, 1); }); @@ -105,14 +98,12 @@ main() { test('should call timers at their scheduled time', () { DateTime calledAt; var periodicCalledAt = []; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { new Timer(advanceBy ~/ 2, () {calledAt = unit.now();}); new Timer.periodic(advanceBy ~/ 2, (_) { periodicCalledAt.add(unit.now());}); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { + return unit.advance(advanceBy); + }).then((_) { expect(calledAt, initialTime.add(advanceBy ~/ 2)); expect(periodicCalledAt, [initialTime.add(advanceBy ~/ 2), initialTime.add(advanceBy)]); @@ -121,7 +112,7 @@ main() { test('should not call timers expiring after end time', () { var timerCallCount = 0; - unit.zone.runGuarded(() { + unit.run(() { new Timer(advanceBy * 2, () {timerCallCount++;}); unit.advance(advanceBy); }); @@ -130,25 +121,21 @@ main() { test('should not call canceled timers', () { int timerCallCount = 0; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { var timer = new Timer(advanceBy ~/ 2, () {timerCallCount++;}); timer.cancel(); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { + return unit.advance(advanceBy); + }).then((_) { expect(timerCallCount, 0); }); }); test('should call periodic timers each time the duration elapses', () { var periodicCallCount = 0; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { new Timer.periodic(advanceBy ~/ 10, (_) {periodicCallCount++;}); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { + return unit.advance(advanceBy); + }).then((_) { expect(periodicCallCount, 10); }); }); @@ -157,25 +144,21 @@ main() { var periodicCallCount = 0; Timer passedTimer; Timer periodic; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { periodic = new Timer.periodic(advanceBy, (timer) {passedTimer = timer;}); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { - expect(periodic, passedTimer); + return unit.advance(advanceBy); + }).then((_) { + expect(periodic, same(passedTimer)); }); }); test('should call microtasks before advancing time', () { - Future advanced; DateTime calledAt; - unit.zone.runGuarded(() { + return unit.run(() { scheduleMicrotask((){ calledAt = unit.now(); }); - advanced = unit.advance(const Duration(minutes: 1)); - }); - return advanced.then((_) { + return unit.advance(const Duration(minutes: 1)); + }).then((_) { expect(calledAt, initialTime); }); }); @@ -185,7 +168,7 @@ main() { var controller = new StreamController(); Future advanced; DateTime heardAt; - unit.zone.runGuarded(() { + unit.run(() { controller.stream.first.then((_) { heardAt = unit.now(); }); controller.add(null); advanced = unit.advance(const Duration(minutes: 1)); @@ -197,24 +180,21 @@ main() { test('should increase negative duration timers to zero duration', () { var negativeDuration = const Duration(days: -1); - Future advanced; DateTime calledAt; - unit.zone.runGuarded(() { + return unit.run(() { new Timer(negativeDuration, () { calledAt = unit.now(); }); - advanced = unit.advance(const Duration(minutes: 1)); - }); - return advanced.then((_) { + return unit.advance(const Duration(minutes: 1)); + }).then((_) { expect(calledAt, initialTime); }); }); test('should not be additive with advanceSync', () { - Future advanced; - unit.zone.runGuarded(() { - advanced = unit.advance(advanceBy); + return unit.run(() { + var advanced = unit.advance(advanceBy); unit.advanceSync(advanceBy * 2); - }); - return advanced.then((_) { + return advanced; + }).then((_) { expect(unit.now(), initialTime.add(advanceBy * 2)); }); }); @@ -223,31 +203,27 @@ main() { test('should be false after timer is run', () { Timer timer; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { timer = new Timer(advanceBy ~/ 2, () {}); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { + return unit.advance(advanceBy); + }).then((_) { expect(timer.isActive, isFalse); }); }); test('should be true after periodic timer is run', () { Timer timer; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { timer = new Timer.periodic(advanceBy ~/ 2, (_) {}); - advanced = unit.advance(advanceBy); - }); - return advanced.then((_) { + return unit.advance(advanceBy); + }).then((_) { expect(timer.isActive, isTrue); }); }); test('should be false after timer is canceled', () { Timer timer; - unit.zone.runGuarded(() { + unit.run(() { timer = new Timer(advanceBy ~/ 2, () {}); timer.cancel(); }); @@ -258,30 +234,27 @@ main() { test('should work with new Future()', () { var callCount = 0; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { new Future(() => callCount++); - advanced = unit.advance(Duration.ZERO); - }); - return advanced.then((_) { + return unit.advance(Duration.ZERO); + }).then((_) { expect(callCount, 1); }); }); test('should work with Future.delayed', () { - Future delayed; - unit.zone.runGuarded(() { - delayed = new Future.delayed(advanceBy, () => 5); - unit.advance(advanceBy); - }); - return delayed.then((e) { - expect(e, 5); + int result; + unit.run(() { + new Future.delayed(advanceBy, () => result = 5); + return unit.advance(advanceBy); + }).then((_) { + expect(result, 5); }); }); test('should work with Future.timeout', () { var completer = new Completer(); - unit.zone.runGuarded(() { + unit.run(() { var timed = completer.future.timeout(advanceBy ~/ 2); new Timer(advanceBy, completer.complete); var advanced = unit.advance(advanceBy); @@ -293,15 +266,13 @@ main() { // it uses `new Stopwatch()`. test('should work with Stream.periodic', () { var events = []; - Future advanced; StreamSubscription subscription; - unit.zone.runGuarded(() { + return unit.run(() { var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); subscription = periodic.listen(events.add, cancelOnError: true); - advanced = unit.advance(const Duration(minutes: 3)); - }); - return advanced.then((_) { + return unit.advance(const Duration(minutes: 3)); + }).then((_) { subscription.cancel(); expect(events, [0, 1, 2]); }); @@ -313,27 +284,22 @@ main() { var errors = []; var controller = new StreamController(); StreamSubscription subscription; - Future advanced; - unit.zone.runGuarded(() { + return unit.run(() { var timed = controller.stream.timeout(const Duration(minutes: 2)); subscription = timed.listen((event) { events.add(event); }, onError: errors.add, cancelOnError: true); controller.add(0); - advanced = unit.advance(const Duration(minutes: 1)); - }); - return advanced.then((_) { + return unit.advance(const Duration(minutes: 1)); + }).then((_) { expect(events, [0]); - Future advanced; - unit.zone.runGuarded(() { - advanced = unit.advance(const Duration(minutes: 1)); - }); - return advanced.then((_) { - subscription.cancel(); - expect(errors, hasLength(1)); - expect(errors.first, new isInstanceOf()); - return controller.close(); - }); + return unit.run(() => unit.advance(const Duration(minutes: 1)) + .then((_) { + subscription.cancel(); + expect(errors, hasLength(1)); + expect(errors.first, new isInstanceOf()); + return controller.close(); + })); }); From adb1ae716309b65f022f428e53a3f88c8934eb23 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 7 Apr 2014 09:15:03 -0500 Subject: [PATCH 009/113] Move FakeTime to quiver.testing.async --- .../lib/{fake_clock.dart => fake_time.dart} | 23 ++++++++++--------- ...ke_clock_test.dart => fake_time_test.dart} | 4 ++-- 2 files changed, 14 insertions(+), 13 deletions(-) rename pkgs/fake_async/lib/{fake_clock.dart => fake_time.dart} (90%) rename pkgs/fake_async/test/{fake_clock_test.dart => fake_time_test.dart} (96%) diff --git a/pkgs/fake_async/lib/fake_clock.dart b/pkgs/fake_async/lib/fake_time.dart similarity index 90% rename from pkgs/fake_async/lib/fake_clock.dart rename to pkgs/fake_async/lib/fake_time.dart index a6d08b0cd..33f690c78 100644 --- a/pkgs/fake_async/lib/fake_clock.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -part of quiver.testing.time; +part of quiver.testing.async; /// A mechanism to make time-dependent units testable. /// @@ -244,16 +244,17 @@ class _FakeTimer implements Timer { final _FakeTime _time; DateTime _nextCall; - _FakeTimer._(this._duration, this._callback, this._isPeriodic, this._time, - this._id) { - // TODO: Figure out how to handle nested or periodic timers with zero - // duration without getting into infinite loop. - // In browser JavaScript, timers can only run every 4 milliseconds once - // sufficiently nested: - // http://www.w3.org/TR/html5/webappapis.html#timer-nesting-level - // What do the dart VM and dart2js timers do here? - _nextCall = _time.now().add(_duration.inMicroseconds.isNegative ? - Duration.ZERO : _duration); + // 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, + this._id) + : _duration = duration < _minDuration ? _minDuration : duration { + _nextCall = _time.now().add(_duration); } bool get isActive => _time._timers.containsKey(_id); diff --git a/pkgs/fake_async/test/fake_clock_test.dart b/pkgs/fake_async/test/fake_time_test.dart similarity index 96% rename from pkgs/fake_async/test/fake_clock_test.dart rename to pkgs/fake_async/test/fake_time_test.dart index d62faa6a0..c289fedc0 100644 --- a/pkgs/fake_async/test/fake_clock_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -library quiver.testing.time.fake_time_test; +library quiver.testing.async.fake_time_test; import 'dart:async'; -import 'package:quiver/testing/time.dart'; +import 'package:quiver/testing/async.dart'; import 'package:unittest/unittest.dart'; main() { From c97ed34ee266217e0484d80d2e76beec2ecc4a88 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 7 Apr 2014 10:10:05 -0500 Subject: [PATCH 010/113] Replace now with elapsed, rename advance to elapse --- pkgs/fake_async/lib/fake_time.dart | 103 ++++++++-------- pkgs/fake_async/test/fake_time_test.dart | 149 +++++++++++------------ 2 files changed, 116 insertions(+), 136 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index 33f690c78..8a46f72ee 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -17,94 +17,89 @@ part of quiver.testing.async; /// A mechanism to make time-dependent units testable. /// /// To use this, test code must be run within a [run] callback. Any [Timer]s -/// created there will be fake. Calling [advance] will then manually advance +/// created there will be fake. Calling [elapse] will then manually elapse /// the time returned by [now], calling any fake timers as they expire. /// -/// Time can also be advanced synchronously ([advanceSync]) to simulate +/// Time can also be elapsed synchronously ([elapseSync]) to simulate /// expensive or blocking calls, in this case timers are not called. /// /// The unit under test can take a [TimeFunction] as a dependency, and /// default it to something like `() => new DateTime.now()` in production, but -/// then have tests pass a closure of [FakeTime.now]. Or for a higher-level -/// interface, see [Clock], which takes a [TimeFunction] as a dependency. +/// then have tests pass something like +/// `() => initialTime.add(fakeTime.elapsed)`. Or for a higher-level interface, +/// see [Clock], which takes a [TimeFunction] as a dependency. abstract class FakeTime { - /// [initialTime] will be the time returned by [now] before any calls to - /// [advance] or [advanceSync]. - factory FakeTime({DateTime initialTime}) = _FakeTime; + factory FakeTime() = _FakeTime; FakeTime._(); - /// Returns the current (fake) time. A time-dependent unit can receive a - /// [TimeFunction] as a dependency. A unit can take one of these as a dependency, and either Pass a closure of this method to the - /// unit under test so that it received a - DateTime now() => _now; + /// Returns the amount of (fake) time that has elapsed. + Duration get elapsed; - /// Simulate the asynchronous advancement of time by [duration]. + /// Simulate the asynchronous elapsation of time by [duration]. /// /// Important: This should only be called from inside a [run] callback. /// /// If [duration] is negative, the returned future completes with an /// [ArgumentError]. /// - /// If the future from the previous call to [advance] has not yet completed, + /// If the future from the previous call to [elapse] has not yet completed, /// the returned future completes with a [StateError]. /// /// Any Timers created within a [run] callback which are scheduled to expire - /// at or before the new time after the advancement, are run, each in their + /// at or before the new time after the elapsement, are run, each in their /// own event loop frame as normal, except that there is no actual delay - /// before each timer run. When a timer is run, `now()` will have been + /// before each timer run. When a timer is run, [elapsed] will have been /// advanced by the timer's specified duration, potentially more if there were - /// calls to [advanceSync] as well. + /// calls to [elapseSync] as well. /// /// When there are no more timers to run, or the next timer is beyond the - /// end time (time when called + [duration]), `now()` is advanced to the end + /// end time (time when called + [duration]), [elapsed] is advanced to the end /// time, and the returned Future is completed. - Future advance(Duration duration); + Future elapse(Duration duration); - /// Simulate the synchronous advancement of this time by [duration]. + /// Simulate the synchronous elapsement of this time by [duration]. /// /// If [duration] is negative, throws an [ArgumentError]. - void advanceSync(Duration duration); + void elapseSync(Duration duration); /// Runs [callback] in a [Zone] which implements /// [ZoneSpecification.createTimer] and /// [ZoneSpecification.createPeriodicTimer] to create timers which will be - /// called during the completion of Futures returned from [advance]. + /// called during the completion of Futures returned from [elapse]. run(callback()); } class _FakeTime extends FakeTime { - DateTime _now; - DateTime _advancingTo; - Completer _advanceCompleter; + Duration _elapsed = Duration.ZERO; + Duration _elapsingTo; + Completer _elapseCompleter; - _FakeTime({DateTime initialTime}) : super._() { - _now = initialTime == null ? new DateTime.now() : initialTime; - } + _FakeTime() : super._(); - DateTime now() => _now; + Duration get elapsed => _elapsed; - Future advance(Duration duration) { + Future elapse(Duration duration) { if (duration.inMicroseconds < 0) { return new Future.error( - new ArgumentError('Cannot call advance with negative duration')); + new ArgumentError('Cannot call elapse with negative duration')); } - if (_advancingTo != null) { + if (_elapsingTo != null) { return new Future.error( - new StateError('Cannot advance until previous advance is complete.')); + new StateError('Cannot elapse until previous elapse is complete.')); } - _advancingTo = _now.add(duration); - _advanceCompleter = new Completer(); - return _advanceCompleter.future; + _elapsingTo = _elapsed + duration; + _elapseCompleter = new Completer(); + return _elapseCompleter.future; } - void advanceSync(Duration duration) { + void elapseSync(Duration duration) { if (duration.inMicroseconds < 0) { - throw new ArgumentError('Cannot call advance with negative duration'); + throw new ArgumentError('Cannot call elapse with negative duration'); } - _now = _now.add(duration); + _elapsed += duration; } run(callback()) { @@ -173,10 +168,8 @@ class _FakeTime extends FakeTime { return ret; }); - _advanceTo(DateTime to) { - if (to.millisecondsSinceEpoch > _now.millisecondsSinceEpoch) { - _now = to; - } + _elapseTo(Duration to) { + if (to > _elapsed) _elapsed = to; } Map _timers = {}; @@ -191,15 +184,15 @@ class _FakeTime extends FakeTime { _scheduleTimer(Zone self, ZoneDelegate parent, Zone zone) { - if (!_waitingForTimer && _advancingTo != null) { + if (!_waitingForTimer && _elapsingTo != null) { var next = _getNextTimer(); var completeTimer = next != null ? self.bindCallback(() => _runTimer(next), runGuarded: true) : () { - _advanceTo(_advancingTo); - _advancingTo = null; - _advanceCompleter.complete(); - _advanceCompleter = null; + _elapseTo(_elapsingTo); + _elapsingTo = null; + _elapseCompleter.complete(); + _elapseCompleter = null; }; parent.createTimer(zone, Duration.ZERO, () { completeTimer(); @@ -212,19 +205,19 @@ class _FakeTime extends FakeTime { _FakeTimer _getNextTimer() { return min(_timers.values.where((timer) => - timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || - (_advancingTo != null && - timer._nextCall.millisecondsSinceEpoch <= - _advancingTo.millisecondsSinceEpoch) + timer._nextCall <= _elapsed || + (_elapsingTo != null && + timer._nextCall <= + _elapsingTo) ), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } _runTimer(_FakeTimer timer) { assert(timer.isActive); - _advanceTo(timer._nextCall); + _elapseTo(timer._nextCall); if (timer._isPeriodic) { timer._callback(timer); - timer._nextCall = timer._nextCall.add(timer._duration); + timer._nextCall += timer._duration; } else { timer._callback(); _timers.remove(timer._id); @@ -242,7 +235,7 @@ class _FakeTimer implements Timer { final Function _callback; final bool _isPeriodic; final _FakeTime _time; - DateTime _nextCall; + Duration _nextCall; // TODO: In browser JavaScript, timers can only run every 4 milliseconds once // sufficiently nested: @@ -254,7 +247,7 @@ class _FakeTimer implements Timer { _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time, this._id) : _duration = duration < _minDuration ? _minDuration : duration { - _nextCall = _time.now().add(_duration); + _nextCall = _time._elapsed + _duration; } bool get isActive => _time._timers.containsKey(_id); diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index c289fedc0..745a23bc9 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -23,59 +23,47 @@ main() { group('FakeTime', () { FakeTime unit; - DateTime initialTime; - Duration advanceBy; + Duration elapseBy; setUp(() { - initialTime = new DateTime(2000); - unit = new FakeTime(initialTime: initialTime); - advanceBy = const Duration(days: 1); + unit = new FakeTime(); + elapseBy = const Duration(days: 1); }); - test('should set initial time', () { - expect(unit.now(), initialTime); - }); - - test('should default initial time to system time', () { - expect( - new FakeTime().now().millisecondsSinceEpoch, - closeTo(new DateTime.now().millisecondsSinceEpoch, 500)); - }); - - group('advanceSync', () { + group('elapseSync', () { - test('should advance time synchronously', () { - unit.advanceSync(advanceBy); - expect(unit.now(), initialTime.add(advanceBy)); + test('should elapse time synchronously', () { + unit.elapseSync(elapseBy); + expect(unit.elapsed, elapseBy); }); test('should throw ArgumentError when called with a negative duration', () { expect(() { - unit.advanceSync(const Duration(days: -1)); + unit.elapseSync(const Duration(days: -1)); }, throwsA(new isInstanceOf())); }); }); - group('advance', () { + group('elapse', () { - test('should advance time asynchronously', () => - unit.run(() => unit.advance(advanceBy)).then((_) { - expect(unit.now(), initialTime.add(advanceBy)); + test('should elapse time asynchronously', () => + unit.run(() => unit.elapse(elapseBy)).then((_) { + expect(unit.elapsed, elapseBy); })); test('should throw ArgumentError when called with a negative duration', () { expect( - unit.advance(const Duration(days: -1)), + unit.elapse(const Duration(days: -1)), throwsA(new isInstanceOf())); }); test('should throw when called before previous call is complete', () { unit.run(() { - unit.advance(advanceBy); - expect(unit.advance(advanceBy), + unit.elapse(elapseBy); + expect(unit.elapse(elapseBy), throwsA(new isInstanceOf())); }); }); @@ -86,9 +74,9 @@ main() { var beforeCallCount = 0; var atCallCount = 0; return unit.run(() { - new Timer(advanceBy ~/ 2, () {beforeCallCount++;}); - new Timer(advanceBy, () {atCallCount++;}); - return unit.advance(advanceBy); + new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); + new Timer(elapseBy, () {atCallCount++;}); + return unit.elapse(elapseBy); }).then((_) { expect(beforeCallCount, 1); expect(atCallCount, 1); @@ -96,25 +84,24 @@ main() { }); test('should call timers at their scheduled time', () { - DateTime calledAt; - var periodicCalledAt = []; + Duration calledAt; + var periodicCalledAt = []; return unit.run(() { - new Timer(advanceBy ~/ 2, () {calledAt = unit.now();}); - new Timer.periodic(advanceBy ~/ 2, (_) { - periodicCalledAt.add(unit.now());}); - return unit.advance(advanceBy); + new Timer(elapseBy ~/ 2, () {calledAt = unit.elapsed;}); + new Timer.periodic(elapseBy ~/ 2, (_) { + periodicCalledAt.add(unit.elapsed);}); + return unit.elapse(elapseBy); }).then((_) { - expect(calledAt, initialTime.add(advanceBy ~/ 2)); - expect(periodicCalledAt, [initialTime.add(advanceBy ~/ 2), - initialTime.add(advanceBy)]); + expect(calledAt, elapseBy ~/ 2); + expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy]); }); }); test('should not call timers expiring after end time', () { var timerCallCount = 0; unit.run(() { - new Timer(advanceBy * 2, () {timerCallCount++;}); - unit.advance(advanceBy); + new Timer(elapseBy * 2, () {timerCallCount++;}); + unit.elapse(elapseBy); }); expect(timerCallCount, 0); }); @@ -122,9 +109,9 @@ main() { test('should not call canceled timers', () { int timerCallCount = 0; return unit.run(() { - var timer = new Timer(advanceBy ~/ 2, () {timerCallCount++;}); + var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); - return unit.advance(advanceBy); + return unit.elapse(elapseBy); }).then((_) { expect(timerCallCount, 0); }); @@ -133,8 +120,8 @@ main() { test('should call periodic timers each time the duration elapses', () { var periodicCallCount = 0; return unit.run(() { - new Timer.periodic(advanceBy ~/ 10, (_) {periodicCallCount++;}); - return unit.advance(advanceBy); + new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); + return unit.elapse(elapseBy); }).then((_) { expect(periodicCallCount, 10); }); @@ -145,57 +132,57 @@ main() { Timer passedTimer; Timer periodic; return unit.run(() { - periodic = new Timer.periodic(advanceBy, + periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); - return unit.advance(advanceBy); + return unit.elapse(elapseBy); }).then((_) { expect(periodic, same(passedTimer)); }); }); test('should call microtasks before advancing time', () { - DateTime calledAt; + Duration calledAt; return unit.run(() { - scheduleMicrotask((){ calledAt = unit.now(); }); - return unit.advance(const Duration(minutes: 1)); + scheduleMicrotask((){ calledAt = unit.elapsed; }); + return unit.elapse(const Duration(minutes: 1)); }).then((_) { - expect(calledAt, initialTime); + expect(calledAt, Duration.ZERO); }); }); test('should add event before advancing time', () { var events = []; var controller = new StreamController(); - Future advanced; - DateTime heardAt; + Future elapsed; + Duration heardAt; unit.run(() { - controller.stream.first.then((_) { heardAt = unit.now(); }); + controller.stream.first.then((_) { heardAt = unit.elapsed; }); controller.add(null); - advanced = unit.advance(const Duration(minutes: 1)); + elapsed = unit.elapse(const Duration(minutes: 1)); }); - return Future.wait([controller.close(), advanced]).then((_) { - expect(heardAt, initialTime); + return Future.wait([controller.close(), elapsed]).then((_) { + expect(heardAt, Duration.ZERO); }); }); test('should increase negative duration timers to zero duration', () { var negativeDuration = const Duration(days: -1); - DateTime calledAt; + Duration calledAt; return unit.run(() { - new Timer(negativeDuration, () { calledAt = unit.now(); }); - return unit.advance(const Duration(minutes: 1)); + new Timer(negativeDuration, () { calledAt = unit.elapsed; }); + return unit.elapse(const Duration(minutes: 1)); }).then((_) { - expect(calledAt, initialTime); + expect(calledAt, Duration.ZERO); }); }); - test('should not be additive with advanceSync', () { + test('should not be additive with elapseSync', () { return unit.run(() { - var advanced = unit.advance(advanceBy); - unit.advanceSync(advanceBy * 2); - return advanced; + var elapsed = unit.elapse(elapseBy); + unit.elapseSync(elapseBy * 2); + return elapsed; }).then((_) { - expect(unit.now(), initialTime.add(advanceBy * 2)); + expect(unit.elapsed, elapseBy * 2); }); }); @@ -204,8 +191,8 @@ main() { test('should be false after timer is run', () { Timer timer; return unit.run(() { - timer = new Timer(advanceBy ~/ 2, () {}); - return unit.advance(advanceBy); + timer = new Timer(elapseBy ~/ 2, () {}); + return unit.elapse(elapseBy); }).then((_) { expect(timer.isActive, isFalse); }); @@ -214,8 +201,8 @@ main() { test('should be true after periodic timer is run', () { Timer timer; return unit.run(() { - timer = new Timer.periodic(advanceBy ~/ 2, (_) {}); - return unit.advance(advanceBy); + timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); + return unit.elapse(elapseBy); }).then((_) { expect(timer.isActive, isTrue); }); @@ -224,7 +211,7 @@ main() { test('should be false after timer is canceled', () { Timer timer; unit.run(() { - timer = new Timer(advanceBy ~/ 2, () {}); + timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); }); expect(timer.isActive, isFalse); @@ -236,7 +223,7 @@ main() { var callCount = 0; return unit.run(() { new Future(() => callCount++); - return unit.advance(Duration.ZERO); + return unit.elapse(Duration.ZERO); }).then((_) { expect(callCount, 1); }); @@ -245,8 +232,8 @@ main() { test('should work with Future.delayed', () { int result; unit.run(() { - new Future.delayed(advanceBy, () => result = 5); - return unit.advance(advanceBy); + new Future.delayed(elapseBy, () => result = 5); + return unit.elapse(elapseBy); }).then((_) { expect(result, 5); }); @@ -255,10 +242,10 @@ main() { test('should work with Future.timeout', () { var completer = new Completer(); unit.run(() { - var timed = completer.future.timeout(advanceBy ~/ 2); - new Timer(advanceBy, completer.complete); - var advanced = unit.advance(advanceBy); - expect(Future.wait([advanced, timed]), throwsA(new isInstanceOf())); + var timed = completer.future.timeout(elapseBy ~/ 2); + new Timer(elapseBy, completer.complete); + var elapsed = unit.elapse(elapseBy); + expect(Future.wait([elapsed, timed]), throwsA(new isInstanceOf())); }); }); @@ -271,7 +258,7 @@ main() { var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); subscription = periodic.listen(events.add, cancelOnError: true); - return unit.advance(const Duration(minutes: 3)); + return unit.elapse(const Duration(minutes: 3)); }).then((_) { subscription.cancel(); expect(events, [0, 1, 2]); @@ -290,10 +277,10 @@ main() { events.add(event); }, onError: errors.add, cancelOnError: true); controller.add(0); - return unit.advance(const Duration(minutes: 1)); + return unit.elapse(const Duration(minutes: 1)); }).then((_) { expect(events, [0]); - return unit.run(() => unit.advance(const Duration(minutes: 1)) + return unit.run(() => unit.elapse(const Duration(minutes: 1)) .then((_) { subscription.cancel(); expect(errors, hasLength(1)); From e0b1ae70cbb9f2eae5c9fd2c5d9007dc04c0737c Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 7 Apr 2014 11:21:23 -0500 Subject: [PATCH 011/113] Pass receiver to run callbacks --- pkgs/fake_async/lib/fake_time.dart | 15 +- pkgs/fake_async/test/fake_time_test.dart | 263 +++++++++++------------ 2 files changed, 141 insertions(+), 137 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index 8a46f72ee..a8c2cf98c 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -28,6 +28,13 @@ part of quiver.testing.async; /// then have tests pass something like /// `() => initialTime.add(fakeTime.elapsed)`. Or for a higher-level interface, /// see [Clock], which takes a [TimeFunction] as a dependency. +/// +/// Example: +/// +/// test('testedFunc', () => new FakeTime()..run((time) { +/// testedFunc(now: () => initialTime.add(time.elapsed)); +/// return time.elapse(duration).then((_) => expect(...)); +/// })); abstract class FakeTime { factory FakeTime() = _FakeTime; @@ -68,7 +75,9 @@ abstract class FakeTime { /// [ZoneSpecification.createTimer] and /// [ZoneSpecification.createPeriodicTimer] to create timers which will be /// called during the completion of Futures returned from [elapse]. - run(callback()); + /// [callback] is called with `this` which facilitates things like + /// `new FakeTime()..run(...)`. + run(callback(FakeTime self)); } class _FakeTime extends FakeTime { @@ -102,11 +111,11 @@ class _FakeTime extends FakeTime { _elapsed += duration; } - run(callback()) { + run(callback(FakeTime self)) { if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); } - return _zone.runGuarded(callback); + return _zone.runGuarded(() => callback(this)); } Zone _zone; diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index 745a23bc9..d0172b591 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -22,23 +22,19 @@ import 'package:unittest/unittest.dart'; main() { group('FakeTime', () { - FakeTime unit; - Duration elapseBy; - - setUp(() { - unit = new FakeTime(); - elapseBy = const Duration(days: 1); - }); + const elapseBy = const Duration(days: 1); group('elapseSync', () { test('should elapse time synchronously', () { + var unit = new FakeTime(); unit.elapseSync(elapseBy); expect(unit.elapsed, elapseBy); }); - test('should throw ArgumentError when called with a negative duration', + test('should throw when called with a negative duration', () { + var unit = new FakeTime(); expect(() { unit.elapseSync(const Duration(days: -1)); }, throwsA(new isInstanceOf())); @@ -48,22 +44,24 @@ main() { group('elapse', () { - test('should elapse time asynchronously', () => - unit.run(() => unit.elapse(elapseBy)).then((_) { - expect(unit.elapsed, elapseBy); - })); + test('should elapse time asynchronously', () { + return new FakeTime()..run((time) => time.elapse(elapseBy).then((_) { + expect(time.elapsed, elapseBy); + })); + }); - test('should throw ArgumentError when called with a negative duration', - () { - expect( - unit.elapse(const Duration(days: -1)), - throwsA(new isInstanceOf())); - }); + test('should throw when called with a negative duration', () { + new FakeTime()..run((time) { + expect( + new FakeTime().elapse(const Duration(days: -1)), + throwsA(new isInstanceOf())); + }); + }); test('should throw when called before previous call is complete', () { - unit.run(() { - unit.elapse(elapseBy); - expect(unit.elapse(elapseBy), + new FakeTime()..run((time) { + time.elapse(elapseBy); + expect(time.elapse(elapseBy), throwsA(new isInstanceOf())); }); }); @@ -71,59 +69,60 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - var beforeCallCount = 0; - var atCallCount = 0; - return unit.run(() { + return new FakeTime()..run((time) { + var beforeCallCount = 0; + var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); new Timer(elapseBy, () {atCallCount++;}); - return unit.elapse(elapseBy); - }).then((_) { - expect(beforeCallCount, 1); - expect(atCallCount, 1); + return time.elapse(elapseBy).then((_) { + expect(beforeCallCount, 1); + expect(atCallCount, 1); + }); }); }); test('should call timers at their scheduled time', () { - Duration calledAt; - var periodicCalledAt = []; - return unit.run(() { - new Timer(elapseBy ~/ 2, () {calledAt = unit.elapsed;}); + return new FakeTime()..run((time) { + Duration calledAt; + var periodicCalledAt = []; + new Timer(elapseBy ~/ 2, () {calledAt = time.elapsed;}); new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(unit.elapsed);}); - return unit.elapse(elapseBy); - }).then((_) { - expect(calledAt, elapseBy ~/ 2); - expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy]); + periodicCalledAt.add(time.elapsed);}); + return time.elapse(elapseBy).then((_) { + expect(calledAt, elapseBy ~/ 2); + expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy]); + }); }); }); test('should not call timers expiring after end time', () { - var timerCallCount = 0; - unit.run(() { + return new FakeTime()..run((time) { + var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); - unit.elapse(elapseBy); + return time.elapse(elapseBy).then((_) { + expect(timerCallCount, 0); + }); }); - expect(timerCallCount, 0); }); test('should not call canceled timers', () { - int timerCallCount = 0; - return unit.run(() { + return new FakeTime()..run((time) { + int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); - return unit.elapse(elapseBy); - }).then((_) { - expect(timerCallCount, 0); + return time.elapse(elapseBy).then((_) { + expect(timerCallCount, 0); + }); }); }); test('should call periodic timers each time the duration elapses', () { - var periodicCallCount = 0; - return unit.run(() { + return new FakeTime()..run((time) { + var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); - return unit.elapse(elapseBy); - }).then((_) { - expect(periodicCallCount, 10); + return time.elapse(elapseBy).then((_) { + expect(periodicCallCount, 10); + }); }); }); @@ -131,86 +130,83 @@ main() { var periodicCallCount = 0; Timer passedTimer; Timer periodic; - return unit.run(() { + return new FakeTime()..run((time) { periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); - return unit.elapse(elapseBy); + return time.elapse(elapseBy); }).then((_) { expect(periodic, same(passedTimer)); }); }); test('should call microtasks before advancing time', () { - Duration calledAt; - return unit.run(() { - scheduleMicrotask((){ calledAt = unit.elapsed; }); - return unit.elapse(const Duration(minutes: 1)); - }).then((_) { - expect(calledAt, Duration.ZERO); + return new FakeTime()..run((time) { + Duration calledAt; + scheduleMicrotask((){ calledAt = time.elapsed; }); + return time.elapse(const Duration(minutes: 1)).then((_) { + expect(calledAt, Duration.ZERO); + }); }); }); test('should add event before advancing time', () { - var events = []; - var controller = new StreamController(); - Future elapsed; - Duration heardAt; - unit.run(() { - controller.stream.first.then((_) { heardAt = unit.elapsed; }); + new FakeTime()..run((time) { + var events = []; + var controller = new StreamController(); + Duration heardAt; + controller.stream.first.then((_) { heardAt = time.elapsed; }); controller.add(null); - elapsed = unit.elapse(const Duration(minutes: 1)); - }); - return Future.wait([controller.close(), elapsed]).then((_) { - expect(heardAt, Duration.ZERO); + var elapsed = time.elapse(const Duration(minutes: 1)); + return Future.wait([controller.close(), elapsed]).then((_) { + expect(heardAt, Duration.ZERO); + }); }); }); test('should increase negative duration timers to zero duration', () { - var negativeDuration = const Duration(days: -1); - Duration calledAt; - return unit.run(() { - new Timer(negativeDuration, () { calledAt = unit.elapsed; }); - return unit.elapse(const Duration(minutes: 1)); - }).then((_) { - expect(calledAt, Duration.ZERO); + return new FakeTime()..run((time) { + var negativeDuration = const Duration(days: -1); + Duration calledAt; + new Timer(negativeDuration, () { calledAt = time.elapsed; }); + return time.elapse(const Duration(minutes: 1)).then((_) { + expect(calledAt, Duration.ZERO); + }); }); }); test('should not be additive with elapseSync', () { - return unit.run(() { - var elapsed = unit.elapse(elapseBy); - unit.elapseSync(elapseBy * 2); - return elapsed; - }).then((_) { - expect(unit.elapsed, elapseBy * 2); + return new FakeTime()..run((time) { + var elapsed = time.elapse(elapseBy); + time.elapseSync(elapseBy * 2); + return elapsed.then((_) { + expect(time.elapsed, elapseBy * 2); + }); }); }); group('isActive', () { test('should be false after timer is run', () { - Timer timer; - return unit.run(() { - timer = new Timer(elapseBy ~/ 2, () {}); - return unit.elapse(elapseBy); - }).then((_) { - expect(timer.isActive, isFalse); + return new FakeTime()..run((time) { + var timer = new Timer(elapseBy ~/ 2, () {}); + return time.elapse(elapseBy).then((_) { + expect(timer.isActive, isFalse); + }); }); }); test('should be true after periodic timer is run', () { - Timer timer; - return unit.run(() { - timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); - return unit.elapse(elapseBy); - }).then((_) { - expect(timer.isActive, isTrue); + return new FakeTime()..run((time) { + var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); + return time.elapse(elapseBy).then((_) { + expect(timer.isActive, isTrue); + }); }); }); test('should be false after timer is canceled', () { Timer timer; - unit.run(() { + new FakeTime()..run((time) { timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); }); @@ -220,73 +216,72 @@ main() { }); test('should work with new Future()', () { - var callCount = 0; - return unit.run(() { + return new FakeTime()..run((time) { + var callCount = 0; new Future(() => callCount++); - return unit.elapse(Duration.ZERO); - }).then((_) { - expect(callCount, 1); + return time.elapse(Duration.ZERO).then((_) { + expect(callCount, 1); + }); }); }); test('should work with Future.delayed', () { - int result; - unit.run(() { + return new FakeTime()..run((time) { + int result; new Future.delayed(elapseBy, () => result = 5); - return unit.elapse(elapseBy); - }).then((_) { - expect(result, 5); + return time.elapse(elapseBy).then((_) { + expect(result, 5); + }); }); }); test('should work with Future.timeout', () { - var completer = new Completer(); - unit.run(() { + new FakeTime()..run((time) { + var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); new Timer(elapseBy, completer.complete); - var elapsed = unit.elapse(elapseBy); - expect(Future.wait([elapsed, timed]), throwsA(new isInstanceOf())); + var elapsed = time.elapse(elapseBy); + expect(Future.wait([elapsed, timed]), + throwsA(new isInstanceOf())); }); }); // TODO: Pausing and resuming the timeout Stream doesn't work since // it uses `new Stopwatch()`. test('should work with Stream.periodic', () { - var events = []; - StreamSubscription subscription; - return unit.run(() { + return new FakeTime()..run((time) { + var events = []; + StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); subscription = periodic.listen(events.add, cancelOnError: true); - return unit.elapse(const Duration(minutes: 3)); - }).then((_) { - subscription.cancel(); - expect(events, [0, 1, 2]); + return time.elapse(const Duration(minutes: 3)).then((_) { + subscription.cancel(); + expect(events, [0, 1, 2]); + }); }); }); test('should work with Stream.timeout', () { - var events = []; - var errors = []; - var controller = new StreamController(); - StreamSubscription subscription; - return unit.run(() { + return new FakeTime()..run((time) { + var events = []; + var errors = []; + var controller = new StreamController(); var timed = controller.stream.timeout(const Duration(minutes: 2)); - subscription = timed.listen((event) { - events.add(event); - }, onError: errors.add, cancelOnError: true); + var subscription = timed.listen(events.add, onError: errors.add, + cancelOnError: true); controller.add(0); - return unit.elapse(const Duration(minutes: 1)); - }).then((_) { - expect(events, [0]); - return unit.run(() => unit.elapse(const Duration(minutes: 1)) - .then((_) { - subscription.cancel(); - expect(errors, hasLength(1)); - expect(errors.first, new isInstanceOf()); - return controller.close(); - })); + return time.elapse(const Duration(minutes: 1)).then((_) { + expect(events, [0]); + return time.elapse(const Duration(minutes: 1)).then((_) { + subscription.cancel(); + expect(errors, hasLength(1)); + expect(errors.first, new isInstanceOf()); + return controller.close(); + }); + + }); }); From 005548004fa771147c8e744310ad0859777cf6df Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 7 Apr 2014 11:46:41 -0500 Subject: [PATCH 012/113] Replace ..run with .run --- pkgs/fake_async/lib/fake_time.dart | 5 ++- pkgs/fake_async/test/fake_time_test.dart | 42 ++++++++++++------------ 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index a8c2cf98c..ce21ac8d4 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -31,7 +31,7 @@ part of quiver.testing.async; /// /// Example: /// -/// test('testedFunc', () => new FakeTime()..run((time) { +/// test('testedFunc', () => new FakeTime().run((time) { /// testedFunc(now: () => initialTime.add(time.elapsed)); /// return time.elapse(duration).then((_) => expect(...)); /// })); @@ -75,8 +75,7 @@ abstract class FakeTime { /// [ZoneSpecification.createTimer] and /// [ZoneSpecification.createPeriodicTimer] to create timers which will be /// called during the completion of Futures returned from [elapse]. - /// [callback] is called with `this` which facilitates things like - /// `new FakeTime()..run(...)`. + /// [callback] is called with `this`. run(callback(FakeTime self)); } diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index d0172b591..70368dfd5 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -45,13 +45,13 @@ main() { group('elapse', () { test('should elapse time asynchronously', () { - return new FakeTime()..run((time) => time.elapse(elapseBy).then((_) { + return new FakeTime().run((time) => time.elapse(elapseBy).then((_) { expect(time.elapsed, elapseBy); })); }); test('should throw when called with a negative duration', () { - new FakeTime()..run((time) { + new FakeTime().run((time) { expect( new FakeTime().elapse(const Duration(days: -1)), throwsA(new isInstanceOf())); @@ -59,7 +59,7 @@ main() { }); test('should throw when called before previous call is complete', () { - new FakeTime()..run((time) { + new FakeTime().run((time) { time.elapse(elapseBy); expect(time.elapse(elapseBy), throwsA(new isInstanceOf())); @@ -69,7 +69,7 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var beforeCallCount = 0; var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); @@ -82,7 +82,7 @@ main() { }); test('should call timers at their scheduled time', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { Duration calledAt; var periodicCalledAt = []; new Timer(elapseBy ~/ 2, () {calledAt = time.elapsed;}); @@ -96,7 +96,7 @@ main() { }); test('should not call timers expiring after end time', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); return time.elapse(elapseBy).then((_) { @@ -106,7 +106,7 @@ main() { }); test('should not call canceled timers', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); @@ -117,7 +117,7 @@ main() { }); test('should call periodic timers each time the duration elapses', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); return time.elapse(elapseBy).then((_) { @@ -130,7 +130,7 @@ main() { var periodicCallCount = 0; Timer passedTimer; Timer periodic; - return new FakeTime()..run((time) { + return new FakeTime().run((time) { periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); return time.elapse(elapseBy); @@ -140,7 +140,7 @@ main() { }); test('should call microtasks before advancing time', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { Duration calledAt; scheduleMicrotask((){ calledAt = time.elapsed; }); return time.elapse(const Duration(minutes: 1)).then((_) { @@ -150,7 +150,7 @@ main() { }); test('should add event before advancing time', () { - new FakeTime()..run((time) { + new FakeTime().run((time) { var events = []; var controller = new StreamController(); Duration heardAt; @@ -164,7 +164,7 @@ main() { }); test('should increase negative duration timers to zero duration', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var negativeDuration = const Duration(days: -1); Duration calledAt; new Timer(negativeDuration, () { calledAt = time.elapsed; }); @@ -175,7 +175,7 @@ main() { }); test('should not be additive with elapseSync', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var elapsed = time.elapse(elapseBy); time.elapseSync(elapseBy * 2); return elapsed.then((_) { @@ -187,7 +187,7 @@ main() { group('isActive', () { test('should be false after timer is run', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); return time.elapse(elapseBy).then((_) { expect(timer.isActive, isFalse); @@ -196,7 +196,7 @@ main() { }); test('should be true after periodic timer is run', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); return time.elapse(elapseBy).then((_) { expect(timer.isActive, isTrue); @@ -206,7 +206,7 @@ main() { test('should be false after timer is canceled', () { Timer timer; - new FakeTime()..run((time) { + new FakeTime().run((time) { timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); }); @@ -216,7 +216,7 @@ main() { }); test('should work with new Future()', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var callCount = 0; new Future(() => callCount++); return time.elapse(Duration.ZERO).then((_) { @@ -226,7 +226,7 @@ main() { }); test('should work with Future.delayed', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { int result; new Future.delayed(elapseBy, () => result = 5); return time.elapse(elapseBy).then((_) { @@ -236,7 +236,7 @@ main() { }); test('should work with Future.timeout', () { - new FakeTime()..run((time) { + new FakeTime().run((time) { var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); new Timer(elapseBy, completer.complete); @@ -249,7 +249,7 @@ main() { // TODO: Pausing and resuming the timeout Stream doesn't work since // it uses `new Stopwatch()`. test('should work with Stream.periodic', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var events = []; StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), @@ -264,7 +264,7 @@ main() { }); test('should work with Stream.timeout', () { - return new FakeTime()..run((time) { + return new FakeTime().run((time) { var events = []; var errors = []; var controller = new StreamController(); From bd645b80c9e00fed2a8feed765b509bf946fb2a4 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Thu, 10 Apr 2014 15:27:25 -0500 Subject: [PATCH 013/113] Revert "Replace now with elapsed, rename advance to elapse" This reverts commit 279b0c1f6d473d65ed4a449e6fb308ff18ac7c9b. Conflicts: lib/testing/src/async/fake_time.dart test/testing/async/fake_time_test.dart --- pkgs/fake_async/lib/fake_time.dart | 69 ++++++------ pkgs/fake_async/test/fake_time_test.dart | 132 ++++++++++++----------- 2 files changed, 109 insertions(+), 92 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index ce21ac8d4..8d66dcb17 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -23,28 +23,29 @@ part of quiver.testing.async; /// Time can also be elapsed synchronously ([elapseSync]) to simulate /// expensive or blocking calls, in this case timers are not called. /// -/// The unit under test can take a [TimeFunction] as a dependency, and -/// default it to something like `() => new DateTime.now()` in production, but -/// then have tests pass something like -/// `() => initialTime.add(fakeTime.elapsed)`. Or for a higher-level interface, -/// see [Clock], which takes a [TimeFunction] as a dependency. +/// The unit under test can take a [Clock] as a dependency, and +/// default it to [SYSTEM_CLOCK] in production, but then have tests pass +/// [FakeTime.clock]. /// /// Example: /// /// test('testedFunc', () => new FakeTime().run((time) { -/// testedFunc(now: () => initialTime.add(time.elapsed)); +/// testedFunc(clock: time.clock); /// return time.elapse(duration).then((_) => expect(...)); /// })); abstract class FakeTime { - factory FakeTime() = _FakeTime; + /// [initialTime] will be the time returned by [now] before any calls to + /// [elapse] or [elapseSync]. + factory FakeTime({DateTime initialTime}) = _FakeTime; FakeTime._(); - /// Returns the amount of (fake) time that has elapsed. - Duration get elapsed; + /// Returns a fake [Clock] whose time elapses along with this Clock. Pass + /// this as a dependency to the unit under test. + Clock get clock; - /// Simulate the asynchronous elapsation of time by [duration]. + /// Simulate the asynchronous elapsement of time by [duration]. /// /// Important: This should only be called from inside a [run] callback. /// @@ -57,12 +58,12 @@ abstract class FakeTime { /// Any Timers created within a [run] callback which are scheduled to expire /// at or before the new time after the elapsement, are run, each in their /// own event loop frame as normal, except that there is no actual delay - /// before each timer run. When a timer is run, [elapsed] will have been - /// advanced by the timer's specified duration, potentially more if there were + /// before each timer run. When a timer is run, `now()` will have been + /// elapsed by the timer's specified duration, potentially more if there were /// calls to [elapseSync] as well. /// /// When there are no more timers to run, or the next timer is beyond the - /// end time (time when called + [duration]), [elapsed] is advanced to the end + /// end time (time when called + [duration]), `now()` is elapsed to the end /// time, and the returned Future is completed. Future elapse(Duration duration); @@ -81,24 +82,26 @@ abstract class FakeTime { class _FakeTime extends FakeTime { - Duration _elapsed = Duration.ZERO; - Duration _elapsingTo; + DateTime _now; + DateTime _advancingTo; Completer _elapseCompleter; - _FakeTime() : super._(); + _FakeTime({DateTime initialTime}) : super._() { + _now = initialTime == null ? new DateTime.now() : initialTime; + } - Duration get elapsed => _elapsed; + Clock get clock => new Clock(() => _now); Future elapse(Duration duration) { if (duration.inMicroseconds < 0) { return new Future.error( new ArgumentError('Cannot call elapse with negative duration')); } - if (_elapsingTo != null) { + if (_advancingTo != null) { return new Future.error( new StateError('Cannot elapse until previous elapse is complete.')); } - _elapsingTo = _elapsed + duration; + _advancingTo = _now.add(duration); _elapseCompleter = new Completer(); return _elapseCompleter.future; } @@ -107,7 +110,7 @@ class _FakeTime extends FakeTime { if (duration.inMicroseconds < 0) { throw new ArgumentError('Cannot call elapse with negative duration'); } - _elapsed += duration; + _now = _now.add(duration); } run(callback(FakeTime self)) { @@ -176,8 +179,10 @@ class _FakeTime extends FakeTime { return ret; }); - _elapseTo(Duration to) { - if (to > _elapsed) _elapsed = to; + _elapseTo(DateTime to) { + if (to.millisecondsSinceEpoch > _now.millisecondsSinceEpoch) { + _now = to; + } } Map _timers = {}; @@ -192,13 +197,13 @@ class _FakeTime extends FakeTime { _scheduleTimer(Zone self, ZoneDelegate parent, Zone zone) { - if (!_waitingForTimer && _elapsingTo != null) { + if (!_waitingForTimer && _advancingTo != null) { var next = _getNextTimer(); var completeTimer = next != null ? self.bindCallback(() => _runTimer(next), runGuarded: true) : () { - _elapseTo(_elapsingTo); - _elapsingTo = null; + _elapseTo(_advancingTo); + _advancingTo = null; _elapseCompleter.complete(); _elapseCompleter = null; }; @@ -213,10 +218,10 @@ class _FakeTime extends FakeTime { _FakeTimer _getNextTimer() { return min(_timers.values.where((timer) => - timer._nextCall <= _elapsed || - (_elapsingTo != null && - timer._nextCall <= - _elapsingTo) + timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || + (_advancingTo != null && + timer._nextCall.millisecondsSinceEpoch <= + _advancingTo.millisecondsSinceEpoch) ), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } @@ -225,7 +230,7 @@ class _FakeTime extends FakeTime { _elapseTo(timer._nextCall); if (timer._isPeriodic) { timer._callback(timer); - timer._nextCall += timer._duration; + timer._nextCall = timer._nextCall.add(timer._duration); } else { timer._callback(); _timers.remove(timer._id); @@ -243,7 +248,7 @@ class _FakeTimer implements Timer { final Function _callback; final bool _isPeriodic; final _FakeTime _time; - Duration _nextCall; + DateTime _nextCall; // TODO: In browser JavaScript, timers can only run every 4 milliseconds once // sufficiently nested: @@ -255,7 +260,7 @@ class _FakeTimer implements Timer { _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time, this._id) : _duration = duration < _minDuration ? _minDuration : duration { - _nextCall = _time._elapsed + _duration; + _nextCall = _time.clock.now().add(_duration); } bool get isActive => _time._timers.containsKey(_id); diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index 70368dfd5..e0c16fee1 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -22,19 +22,35 @@ import 'package:unittest/unittest.dart'; main() { group('FakeTime', () { - const elapseBy = const Duration(days: 1); + FakeTime unit; + DateTime initialTime; + Duration elapseBy; + + setUp(() { + initialTime = new DateTime(2000); + unit = new FakeTime(initialTime: initialTime); + elapseBy = const Duration(days: 1); + }); + + test('should set initial time', () { + expect(unit.clock.now(), initialTime); + }); + + test('should default initial time to system time', () { + expect( + new FakeTime().clock.now().millisecondsSinceEpoch, + closeTo(new DateTime.now().millisecondsSinceEpoch, 500)); + }); group('elapseSync', () { test('should elapse time synchronously', () { - var unit = new FakeTime(); unit.elapseSync(elapseBy); - expect(unit.elapsed, elapseBy); + expect(unit.clock.now(), initialTime.add(elapseBy)); }); test('should throw when called with a negative duration', () { - var unit = new FakeTime(); expect(() { unit.elapseSync(const Duration(days: -1)); }, throwsA(new isInstanceOf())); @@ -44,22 +60,20 @@ main() { group('elapse', () { - test('should elapse time asynchronously', () { - return new FakeTime().run((time) => time.elapse(elapseBy).then((_) { - expect(time.elapsed, elapseBy); - })); - }); + test('should elapse time asynchronously', () => + unit.run((time) => time.elapse(elapseBy)).then((_) { + expect(unit.clock.now(), initialTime.add(elapseBy)); + })); - test('should throw when called with a negative duration', () { - new FakeTime().run((time) { - expect( - new FakeTime().elapse(const Duration(days: -1)), - throwsA(new isInstanceOf())); - }); - }); + test('should throw ArgumentError when called with a negative duration', + () { + expect( + unit.elapse(const Duration(days: -1)), + throwsA(new isInstanceOf())); + }); test('should throw when called before previous call is complete', () { - new FakeTime().run((time) { + unit.run((time) { time.elapse(elapseBy); expect(time.elapse(elapseBy), throwsA(new isInstanceOf())); @@ -69,7 +83,7 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - return new FakeTime().run((time) { + return unit.run((time) { var beforeCallCount = 0; var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); @@ -82,21 +96,22 @@ main() { }); test('should call timers at their scheduled time', () { - return new FakeTime().run((time) { - Duration calledAt; - var periodicCalledAt = []; - new Timer(elapseBy ~/ 2, () {calledAt = time.elapsed;}); + return unit.run((time) { + DateTime calledAt; + var periodicCalledAt = []; + new Timer(elapseBy ~/ 2, () {calledAt = time.clock.now();}); new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(time.elapsed);}); + periodicCalledAt.add(time.clock.now());}); return time.elapse(elapseBy).then((_) { - expect(calledAt, elapseBy ~/ 2); - expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy]); + expect(calledAt, initialTime.add(elapseBy ~/ 2)); + expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] + .map(initialTime.add)); }); }); }); test('should not call timers expiring after end time', () { - return new FakeTime().run((time) { + return unit.run((time) { var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); return time.elapse(elapseBy).then((_) { @@ -106,7 +121,7 @@ main() { }); test('should not call canceled timers', () { - return new FakeTime().run((time) { + return unit.run((time) { int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); @@ -117,7 +132,7 @@ main() { }); test('should call periodic timers each time the duration elapses', () { - return new FakeTime().run((time) { + return unit.run((time) { var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); return time.elapse(elapseBy).then((_) { @@ -127,59 +142,57 @@ main() { }); test('should pass the periodic timer itself to callbacks', () { - var periodicCallCount = 0; - Timer passedTimer; - Timer periodic; - return new FakeTime().run((time) { - periodic = new Timer.periodic(elapseBy, + return unit.run((time) { + Timer passedTimer; + Timer periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); - return time.elapse(elapseBy); - }).then((_) { - expect(periodic, same(passedTimer)); + return time.elapse(elapseBy).then((_) { + expect(periodic, same(passedTimer)); + }); }); }); test('should call microtasks before advancing time', () { - return new FakeTime().run((time) { - Duration calledAt; - scheduleMicrotask((){ calledAt = time.elapsed; }); + return unit.run((time) { + DateTime calledAt; + scheduleMicrotask((){ calledAt = time.clock.now(); }); return time.elapse(const Duration(minutes: 1)).then((_) { - expect(calledAt, Duration.ZERO); + expect(calledAt, initialTime); }); }); }); test('should add event before advancing time', () { - new FakeTime().run((time) { + return unit.run((time) { var events = []; var controller = new StreamController(); - Duration heardAt; - controller.stream.first.then((_) { heardAt = time.elapsed; }); + DateTime heardAt; + controller.stream.first.then((_) { heardAt = time.clock.now(); }); controller.add(null); var elapsed = time.elapse(const Duration(minutes: 1)); return Future.wait([controller.close(), elapsed]).then((_) { - expect(heardAt, Duration.ZERO); + expect(heardAt, initialTime); }); }); }); test('should increase negative duration timers to zero duration', () { - return new FakeTime().run((time) { + return unit.run((time) { var negativeDuration = const Duration(days: -1); - Duration calledAt; - new Timer(negativeDuration, () { calledAt = time.elapsed; }); + DateTime calledAt; + new Timer(negativeDuration, () { calledAt = time.clock.now(); }); return time.elapse(const Duration(minutes: 1)).then((_) { - expect(calledAt, Duration.ZERO); + expect(calledAt, initialTime); }); }); }); test('should not be additive with elapseSync', () { - return new FakeTime().run((time) { + return unit.run((time) { var elapsed = time.elapse(elapseBy); time.elapseSync(elapseBy * 2); return elapsed.then((_) { - expect(time.elapsed, elapseBy * 2); + expect(time.clock.now(), initialTime.add(elapseBy * 2)); }); }); }); @@ -187,7 +200,7 @@ main() { group('isActive', () { test('should be false after timer is run', () { - return new FakeTime().run((time) { + return unit.run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); return time.elapse(elapseBy).then((_) { expect(timer.isActive, isFalse); @@ -196,7 +209,7 @@ main() { }); test('should be true after periodic timer is run', () { - return new FakeTime().run((time) { + return unit.run((time) { var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); return time.elapse(elapseBy).then((_) { expect(timer.isActive, isTrue); @@ -205,18 +218,17 @@ main() { }); test('should be false after timer is canceled', () { - Timer timer; new FakeTime().run((time) { - timer = new Timer(elapseBy ~/ 2, () {}); + var timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); + expect(timer.isActive, isFalse); }); - expect(timer.isActive, isFalse); }); }); test('should work with new Future()', () { - return new FakeTime().run((time) { + return unit.run((time) { var callCount = 0; new Future(() => callCount++); return time.elapse(Duration.ZERO).then((_) { @@ -226,7 +238,7 @@ main() { }); test('should work with Future.delayed', () { - return new FakeTime().run((time) { + return unit.run((time) { int result; new Future.delayed(elapseBy, () => result = 5); return time.elapse(elapseBy).then((_) { @@ -236,7 +248,7 @@ main() { }); test('should work with Future.timeout', () { - new FakeTime().run((time) { + unit.run((time) { var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); new Timer(elapseBy, completer.complete); @@ -249,7 +261,7 @@ main() { // TODO: Pausing and resuming the timeout Stream doesn't work since // it uses `new Stopwatch()`. test('should work with Stream.periodic', () { - return new FakeTime().run((time) { + return unit.run((time) { var events = []; StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), @@ -264,7 +276,7 @@ main() { }); test('should work with Stream.timeout', () { - return new FakeTime().run((time) { + return unit.run((time) { var events = []; var errors = []; var controller = new StreamController(); From a448454495f64d735cb92d2bc55d6bb2805df215 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Fri, 11 Apr 2014 14:53:54 -0500 Subject: [PATCH 014/113] Make elapse sync, rename elapseSync to elapseBlocking --- pkgs/fake_async/lib/fake_time.dart | 125 ++++++---------- pkgs/fake_async/test/fake_time_test.dart | 180 +++++++++++------------ 2 files changed, 124 insertions(+), 181 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index 8d66dcb17..8a2e558ac 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -20,8 +20,8 @@ part of quiver.testing.async; /// created there will be fake. Calling [elapse] will then manually elapse /// the time returned by [now], calling any fake timers as they expire. /// -/// Time can also be elapsed synchronously ([elapseSync]) to simulate -/// expensive or blocking calls, in this case timers are not called. +/// Time can also be elapsed synchronously ([elapseBlocking]) to simulate +/// blocking or expensive calls, in this case timers are not called. /// /// The unit under test can take a [Clock] as a dependency, and /// default it to [SYSTEM_CLOCK] in production, but then have tests pass @@ -36,7 +36,7 @@ part of quiver.testing.async; abstract class FakeTime { /// [initialTime] will be the time returned by [now] before any calls to - /// [elapse] or [elapseSync]. + /// [elapse] or [elapseBlocking]. factory FakeTime({DateTime initialTime}) = _FakeTime; FakeTime._(); @@ -60,17 +60,17 @@ abstract class FakeTime { /// own event loop frame as normal, except that there is no actual delay /// before each timer run. When a timer is run, `now()` will have been /// elapsed by the timer's specified duration, potentially more if there were - /// calls to [elapseSync] as well. + /// calls to [elapseBlocking] as well. /// /// When there are no more timers to run, or the next timer is beyond the /// end time (time when called + [duration]), `now()` is elapsed to the end /// time, and the returned Future is completed. - Future elapse(Duration duration); + void elapse(Duration duration); - /// Simulate the synchronous elapsement of this time by [duration]. + /// Simulate a blocking or expensive call, which causes [duration] to elapse. /// /// If [duration] is negative, throws an [ArgumentError]. - void elapseSync(Duration duration); + void elapseBlocking(Duration duration); /// Runs [callback] in a [Zone] which implements /// [ZoneSpecification.createTimer] and @@ -83,8 +83,7 @@ abstract class FakeTime { class _FakeTime extends FakeTime { DateTime _now; - DateTime _advancingTo; - Completer _elapseCompleter; + DateTime _elapsingTo; _FakeTime({DateTime initialTime}) : super._() { _now = initialTime == null ? new DateTime.now() : initialTime; @@ -92,21 +91,25 @@ class _FakeTime extends FakeTime { Clock get clock => new Clock(() => _now); - Future elapse(Duration duration) { + void elapse(Duration duration) { if (duration.inMicroseconds < 0) { - return new Future.error( - new ArgumentError('Cannot call elapse with negative duration')); + throw new ArgumentError('Cannot call elapse with negative duration'); + } + if (_elapsingTo != null) { + throw new StateError('Cannot elapse until previous elapse is complete.'); } - if (_advancingTo != null) { - return new Future.error( - new StateError('Cannot elapse until previous elapse is complete.')); + _elapsingTo = _now.add(duration); + _drainMicrotasks(); + Timer next; + while ((next = _getNextTimer()) != null) { + _runTimer(next); + _drainMicrotasks(); } - _advancingTo = _now.add(duration); - _elapseCompleter = new Completer(); - return _elapseCompleter.future; + _elapseTo(_elapsingTo); + _elapsingTo = null; } - void elapseSync(Duration duration) { + void elapseBlocking(Duration duration) { if (duration.inMicroseconds < 0) { throw new ArgumentError('Cannot call elapse with negative duration'); } @@ -124,59 +127,26 @@ class _FakeTime extends FakeTime { ZoneSpecification get _zoneSpec => new ZoneSpecification( createTimer: ( Zone self, - ZoneDelegate parent, - Zone zone, + __, + ___, Duration duration, Function callback) { - var bound = self.bindCallback(callback, runGuarded: true); - return _createTimer(duration, bound, false); + return _createTimer(duration, callback, false); }, createPeriodicTimer: ( Zone self, - ZoneDelegate parent, - Zone zone, + __, + ___, Duration duration, Function callback) { - var bound = self.bindUnaryCallback(callback, runGuarded: true); - return _createTimer(duration, bound, true); + return _createTimer(duration, callback, true); }, scheduleMicrotask: ( Zone self, - ZoneDelegate parent, - Zone zone, + __, + ___, Function microtask) { - var bound = self.bindCallback(microtask, runGuarded: true); - parent.scheduleMicrotask(zone, bound); - }, - run: ( - Zone self, - ZoneDelegate parent, - Zone zone, - Function f) { - var ret = parent.run(zone, f); - _scheduleTimer(self, parent, zone); - return ret; - }, - runUnary: ( - Zone self, - ZoneDelegate parent, - Zone zone, - Function f, - arg) { - var ret = parent.runUnary(zone, f, arg); - _scheduleTimer(self, parent, zone); - return ret; - }, - runBinary: ( - Zone self, - ZoneDelegate parent, - Zone zone, - Function f, - arg1, - arg2) { - var ret = parent.runBinary(zone, f, arg1, arg2); - _scheduleTimer(self, parent, zone); - return ret; + _microTasks.add(microtask); }); _elapseTo(DateTime to) { @@ -185,6 +155,8 @@ class _FakeTime extends FakeTime { } } + Queue _microTasks = new Queue(); + Map _timers = {}; var _nextTimerId = 1; bool _waitingForTimer = false; @@ -195,33 +167,12 @@ class _FakeTime extends FakeTime { new _FakeTimer._(duration, callback, isPeriodic, this, id); } - _scheduleTimer(Zone self, ZoneDelegate parent, Zone zone) { - - if (!_waitingForTimer && _advancingTo != null) { - var next = _getNextTimer(); - var completeTimer = next != null ? - self.bindCallback(() => _runTimer(next), runGuarded: true) : - () { - _elapseTo(_advancingTo); - _advancingTo = null; - _elapseCompleter.complete(); - _elapseCompleter = null; - }; - parent.createTimer(zone, Duration.ZERO, () { - completeTimer(); - _waitingForTimer = false; - }); - - _waitingForTimer = true; - } - } - _FakeTimer _getNextTimer() { return min(_timers.values.where((timer) => timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || - (_advancingTo != null && + (_elapsingTo != null && timer._nextCall.millisecondsSinceEpoch <= - _advancingTo.millisecondsSinceEpoch) + _elapsingTo.millisecondsSinceEpoch) ), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } @@ -237,6 +188,12 @@ class _FakeTime extends FakeTime { } } + _drainMicrotasks() { + while(_microTasks.isNotEmpty) { + _microTasks.removeFirst()(); + } + } + _cancelTimer(_FakeTimer timer) => _timers.remove(timer._id); } diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index e0c16fee1..9085d6b36 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -42,17 +42,17 @@ main() { closeTo(new DateTime.now().millisecondsSinceEpoch, 500)); }); - group('elapseSync', () { + group('elapseBlocking', () { test('should elapse time synchronously', () { - unit.elapseSync(elapseBy); + unit.elapseBlocking(elapseBy); expect(unit.clock.now(), initialTime.add(elapseBy)); }); test('should throw when called with a negative duration', () { expect(() { - unit.elapseSync(const Duration(days: -1)); + unit.elapseBlocking(const Duration(days: -1)); }, throwsA(new isInstanceOf())); }); @@ -60,165 +60,159 @@ main() { group('elapse', () { - test('should elapse time asynchronously', () => - unit.run((time) => time.elapse(elapseBy)).then((_) { - expect(unit.clock.now(), initialTime.add(elapseBy)); - })); + test('should elapse time asynchronously', () { + unit.run((time) { + time.elapse(elapseBy); + expect(unit.clock.now(), initialTime.add(elapseBy)); + }); + }); test('should throw ArgumentError when called with a negative duration', () { expect( - unit.elapse(const Duration(days: -1)), + () => unit.elapse(const Duration(days: -1)), throwsA(new isInstanceOf())); }); test('should throw when called before previous call is complete', () { unit.run((time) { + var error; + new Timer(elapseBy ~/ 2, () { + try { time.elapse(elapseBy); } + catch (e) { + error = e; + } + }); time.elapse(elapseBy); - expect(time.elapse(elapseBy), - throwsA(new isInstanceOf())); + expect(error, new isInstanceOf()); }); }); group('when creating timers', () { test('should call timers expiring before or at end time', () { - return unit.run((time) { + unit.run((time) { var beforeCallCount = 0; var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); new Timer(elapseBy, () {atCallCount++;}); - return time.elapse(elapseBy).then((_) { - expect(beforeCallCount, 1); - expect(atCallCount, 1); - }); + time.elapse(elapseBy); + expect(beforeCallCount, 1); + expect(atCallCount, 1); }); }); test('should call timers at their scheduled time', () { - return unit.run((time) { + unit.run((time) { DateTime calledAt; var periodicCalledAt = []; new Timer(elapseBy ~/ 2, () {calledAt = time.clock.now();}); new Timer.periodic(elapseBy ~/ 2, (_) { periodicCalledAt.add(time.clock.now());}); - return time.elapse(elapseBy).then((_) { - expect(calledAt, initialTime.add(elapseBy ~/ 2)); - expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] - .map(initialTime.add)); - }); + time.elapse(elapseBy); + expect(calledAt, initialTime.add(elapseBy ~/ 2)); + expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] + .map(initialTime.add)); }); }); test('should not call timers expiring after end time', () { - return unit.run((time) { + unit.run((time) { var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); - return time.elapse(elapseBy).then((_) { - expect(timerCallCount, 0); - }); + time.elapse(elapseBy); + expect(timerCallCount, 0); }); }); test('should not call canceled timers', () { - return unit.run((time) { + unit.run((time) { int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); - return time.elapse(elapseBy).then((_) { - expect(timerCallCount, 0); - }); + time.elapse(elapseBy); + expect(timerCallCount, 0); }); }); test('should call periodic timers each time the duration elapses', () { - return unit.run((time) { + unit.run((time) { var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); - return time.elapse(elapseBy).then((_) { - expect(periodicCallCount, 10); - }); + time.elapse(elapseBy); + expect(periodicCallCount, 10); }); }); test('should pass the periodic timer itself to callbacks', () { - return unit.run((time) { + unit.run((time) { Timer passedTimer; Timer periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); - return time.elapse(elapseBy).then((_) { - expect(periodic, same(passedTimer)); - }); + time.elapse(elapseBy); + expect(periodic, same(passedTimer)); }); }); test('should call microtasks before advancing time', () { - return unit.run((time) { + unit.run((time) { DateTime calledAt; scheduleMicrotask((){ calledAt = time.clock.now(); }); - return time.elapse(const Duration(minutes: 1)).then((_) { - expect(calledAt, initialTime); - }); + time.elapse(const Duration(minutes: 1)); + expect(calledAt, initialTime); }); }); test('should add event before advancing time', () { return unit.run((time) { - var events = []; var controller = new StreamController(); - DateTime heardAt; - controller.stream.first.then((_) { heardAt = time.clock.now(); }); - controller.add(null); - var elapsed = time.elapse(const Duration(minutes: 1)); - return Future.wait([controller.close(), elapsed]).then((_) { - expect(heardAt, initialTime); + var ret = controller.stream.first.then((_) { + expect(time.clock.now(), initialTime); }); + controller.add(null); + time.elapse(const Duration(minutes: 1)); + return ret; }); }); test('should increase negative duration timers to zero duration', () { - return unit.run((time) { + unit.run((time) { var negativeDuration = const Duration(days: -1); DateTime calledAt; new Timer(negativeDuration, () { calledAt = time.clock.now(); }); - return time.elapse(const Duration(minutes: 1)).then((_) { - expect(calledAt, initialTime); - }); + time.elapse(const Duration(minutes: 1)); + expect(calledAt, initialTime); }); }); - test('should not be additive with elapseSync', () { - return unit.run((time) { - var elapsed = time.elapse(elapseBy); - time.elapseSync(elapseBy * 2); - return elapsed.then((_) { - expect(time.clock.now(), initialTime.add(elapseBy * 2)); - }); + test('should not be additive with elapseBlocking', () { + unit.run((time) { + new Timer(Duration.ZERO, () => time.elapseBlocking(elapseBy * 5)); + time.elapse(elapseBy); + expect(time.clock.now(), initialTime.add(elapseBy * 5)); }); }); group('isActive', () { test('should be false after timer is run', () { - return unit.run((time) { + unit.run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); - return time.elapse(elapseBy).then((_) { - expect(timer.isActive, isFalse); - }); + time.elapse(elapseBy); + expect(timer.isActive, isFalse); }); }); test('should be true after periodic timer is run', () { - return unit.run((time) { + unit.run((time) { var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); - return time.elapse(elapseBy).then((_) { - expect(timer.isActive, isTrue); - }); + time.elapse(elapseBy); + expect(timer.isActive, isTrue); }); }); test('should be false after timer is canceled', () { - new FakeTime().run((time) { + unit.run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); expect(timer.isActive, isFalse); @@ -228,22 +222,20 @@ main() { }); test('should work with new Future()', () { - return unit.run((time) { + unit.run((time) { var callCount = 0; new Future(() => callCount++); - return time.elapse(Duration.ZERO).then((_) { - expect(callCount, 1); - }); + time.elapse(Duration.ZERO); + expect(callCount, 1); }); }); test('should work with Future.delayed', () { - return unit.run((time) { + unit.run((time) { int result; new Future.delayed(elapseBy, () => result = 5); - return time.elapse(elapseBy).then((_) { - expect(result, 5); - }); + time.elapse(elapseBy); + expect(result, 5); }); }); @@ -251,32 +243,30 @@ main() { unit.run((time) { var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); - new Timer(elapseBy, completer.complete); - var elapsed = time.elapse(elapseBy); - expect(Future.wait([elapsed, timed]), - throwsA(new isInstanceOf())); + expect(timed, throwsA(new isInstanceOf())); + time.elapse(elapseBy); + completer.complete(); }); }); // TODO: Pausing and resuming the timeout Stream doesn't work since // it uses `new Stopwatch()`. test('should work with Stream.periodic', () { - return unit.run((time) { + unit.run((time) { var events = []; StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); subscription = periodic.listen(events.add, cancelOnError: true); - return time.elapse(const Duration(minutes: 3)).then((_) { - subscription.cancel(); - expect(events, [0, 1, 2]); - }); + time.elapse(const Duration(minutes: 3)); + subscription.cancel(); + expect(events, [0, 1, 2]); }); }); test('should work with Stream.timeout', () { - return unit.run((time) { + unit.run((time) { var events = []; var errors = []; var controller = new StreamController(); @@ -284,17 +274,13 @@ main() { var subscription = timed.listen(events.add, onError: errors.add, cancelOnError: true); controller.add(0); - return time.elapse(const Duration(minutes: 1)).then((_) { - expect(events, [0]); - return time.elapse(const Duration(minutes: 1)).then((_) { - subscription.cancel(); - expect(errors, hasLength(1)); - expect(errors.first, new isInstanceOf()); - return controller.close(); - }); - - }); - + time.elapse(const Duration(minutes: 1)); + expect(events, [0]); + time.elapse(const Duration(minutes: 1)); + subscription.cancel(); + expect(errors, hasLength(1)); + expect(errors.first, new isInstanceOf()); + return controller.close(); }); }); From b6308102a44a339fa835ddb002ef2b093d9640fe Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Fri, 11 Apr 2014 15:20:26 -0500 Subject: [PATCH 015/113] Test that elapseBlocking doesn't call timers --- pkgs/fake_async/test/fake_time_test.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index 9085d6b36..047377f8b 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -44,7 +44,14 @@ main() { group('elapseBlocking', () { - test('should elapse time synchronously', () { + test('should elapse time without calling timers', () { + var timerCalled = false; + new Timer(elapseBy ~/ 2, () => timerCalled = true); + unit.elapseBlocking(elapseBy); + expect(timerCalled, isFalse); + }); + + test('should elapse time by the specified amount', () { unit.elapseBlocking(elapseBy); expect(unit.clock.now(), initialTime.add(elapseBy)); }); @@ -60,7 +67,7 @@ main() { group('elapse', () { - test('should elapse time asynchronously', () { + test('should elapse time by the specified amount', () { unit.run((time) { time.elapse(elapseBy); expect(unit.clock.now(), initialTime.add(elapseBy)); From d1a8960ab8b5d9d501fe8b04f88de0299a083839 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 14 Apr 2014 08:39:43 -0500 Subject: [PATCH 016/113] Store fake timers in Set instead of Map --- pkgs/fake_async/lib/fake_time.dart | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index 8a2e558ac..99a76acb0 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -157,18 +157,17 @@ class _FakeTime extends FakeTime { Queue _microTasks = new Queue(); - Map _timers = {}; - var _nextTimerId = 1; + Set<_FakeTimer> _timers = new Set<_FakeTimer>(); bool _waitingForTimer = false; - _createTimer(Duration duration, Function callback, bool isPeriodic) { - var id = _nextTimerId++; - return _timers[id] = - new _FakeTimer._(duration, callback, isPeriodic, this, id); + 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.values.where((timer) => + return min(_timers.where((timer) => timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || (_elapsingTo != null && timer._nextCall.millisecondsSinceEpoch <= @@ -184,7 +183,7 @@ class _FakeTime extends FakeTime { timer._nextCall = timer._nextCall.add(timer._duration); } else { timer._callback(); - _timers.remove(timer._id); + _timers.remove(timer); } } @@ -194,13 +193,12 @@ class _FakeTime extends FakeTime { } } - _cancelTimer(_FakeTimer timer) => _timers.remove(timer._id); + _cancelTimer(_FakeTimer timer) => _timers.remove(timer); } class _FakeTimer implements Timer { - final int _id; final Duration _duration; final Function _callback; final bool _isPeriodic; @@ -214,13 +212,12 @@ class _FakeTimer implements Timer { // What do the dart VM and dart2js timers do here? static const _minDuration = Duration.ZERO; - _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time, - this._id) + _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time) : _duration = duration < _minDuration ? _minDuration : duration { _nextCall = _time.clock.now().add(_duration); } - bool get isActive => _time._timers.containsKey(_id); + bool get isActive => _time._timers.contains(this); cancel() => _time._cancelTimer(this); } From 8c3b9f04e84f593fd300dad9956f83b6b7951b38 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 14 Apr 2014 13:06:47 -0500 Subject: [PATCH 017/113] Cleanups, test additions --- pkgs/fake_async/lib/fake_time.dart | 101 +++++++++++++---------- pkgs/fake_async/test/fake_time_test.dart | 101 +++++++++++++++-------- 2 files changed, 121 insertions(+), 81 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index 99a76acb0..0270ffde5 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -16,23 +16,26 @@ part of quiver.testing.async; /// A mechanism to make time-dependent units testable. /// -/// To use this, test code must be run within a [run] callback. Any [Timer]s -/// created there will be fake. Calling [elapse] will then manually elapse -/// the time returned by [now], calling any fake timers as they expire. +/// 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. /// -/// Time can also be elapsed synchronously ([elapseBlocking]) to simulate -/// blocking or expensive calls, in this case timers are not called. +/// The synchronous passage of time (blocking or expensive calls) can also be +/// simulated using [elapseBlocking]. /// -/// The unit under test can take a [Clock] as a dependency, and -/// default it to [SYSTEM_CLOCK] in production, but then have tests pass -/// [FakeTime.clock]. +/// To allow the unit under test to tell time, it can receive a [Clock] as a +/// dependency, and default it to [SYSTEM_CLOCK] in production, but then use +/// [clock] in test code. /// /// Example: /// -/// test('testedFunc', () => new FakeTime().run((time) { -/// testedFunc(clock: time.clock); -/// return time.elapse(duration).then((_) => expect(...)); -/// })); +/// test('testedFunc', () { +/// new FakeTime().run((time) { +/// testedFunc(clock: time.clock); +/// time.elapse(duration); +/// expect(...) +/// }); +/// }); abstract class FakeTime { /// [initialTime] will be the time returned by [now] before any calls to @@ -45,38 +48,45 @@ abstract class FakeTime { /// this as a dependency to the unit under test. Clock get clock; - /// Simulate the asynchronous elapsement of time by [duration]. + /// Simulates the asynchronous passage of time. /// - /// Important: This should only be called from inside a [run] callback. + /// **This should only be called from within the zone used by [run].** /// /// If [duration] is negative, the returned future completes with an /// [ArgumentError]. /// - /// If the future from the previous call to [elapse] has not yet completed, - /// the returned future completes with a [StateError]. + /// If a previous call to [elapse] has not yet completed, throws a + /// [StateError]. /// - /// Any Timers created within a [run] callback which are scheduled to expire - /// at or before the new time after the elapsement, are run, each in their - /// own event loop frame as normal, except that there is no actual delay - /// before each timer run. When a timer is run, `now()` will have been - /// elapsed by the timer's specified duration, potentially more if there were - /// calls to [elapseBlocking] as well. + /// 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. /// - /// When there are no more timers to run, or the next timer is beyond the - /// end time (time when called + [duration]), `now()` is elapsed to the end - /// time, and the returned Future is completed. + /// Once all expired timers are processed, the [clock] is advanced (if + /// necessary) to the time this method was called + [duration]. void elapse(Duration duration); - /// Simulate a blocking or expensive call, which causes [duration] to elapse. + /// 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] which implements - /// [ZoneSpecification.createTimer] and - /// [ZoneSpecification.createPeriodicTimer] to create timers which will be - /// called during the completion of Futures returned from [elapse]. - /// [callback] is called with `this`. + /// 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(FakeTime self)); } @@ -114,6 +124,9 @@ class _FakeTime extends FakeTime { throw new ArgumentError('Cannot call elapse with negative duration'); } _now = _now.add(duration); + if (_elapsingTo != null && _now.isAfter(_elapsingTo)) { + _elapsingTo = _now; + } } run(callback(FakeTime self)) { @@ -126,7 +139,7 @@ class _FakeTime extends FakeTime { ZoneSpecification get _zoneSpec => new ZoneSpecification( createTimer: ( - Zone self, + _, __, ___, Duration duration, @@ -134,7 +147,7 @@ class _FakeTime extends FakeTime { return _createTimer(duration, callback, false); }, createPeriodicTimer: ( - Zone self, + _, __, ___, Duration duration, @@ -142,11 +155,11 @@ class _FakeTime extends FakeTime { return _createTimer(duration, callback, true); }, scheduleMicrotask: ( - Zone self, + _, __, ___, Function microtask) { - _microTasks.add(microtask); + _microtasks.add(microtask); }); _elapseTo(DateTime to) { @@ -155,7 +168,7 @@ class _FakeTime extends FakeTime { } } - Queue _microTasks = new Queue(); + Queue _microtasks = new Queue(); Set<_FakeTimer> _timers = new Set<_FakeTimer>(); bool _waitingForTimer = false; @@ -167,12 +180,8 @@ class _FakeTime extends FakeTime { } _FakeTimer _getNextTimer() { - return min(_timers.where((timer) => - timer._nextCall.millisecondsSinceEpoch <= _now.millisecondsSinceEpoch || - (_elapsingTo != null && - timer._nextCall.millisecondsSinceEpoch <= - _elapsingTo.millisecondsSinceEpoch) - ), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); + return min(_timers.where((timer) => !timer._nextCall.isAfter(_elapsingTo)), + (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } _runTimer(_FakeTimer timer) { @@ -188,11 +197,13 @@ class _FakeTime extends FakeTime { } _drainMicrotasks() { - while(_microTasks.isNotEmpty) { - _microTasks.removeFirst()(); + while (_microtasks.isNotEmpty) { + _microtasks.removeFirst()(); } } + _hasTimer(_FakeTimer timer) => _timers.contains(timer); + _cancelTimer(_FakeTimer timer) => _timers.remove(timer); } @@ -217,7 +228,7 @@ class _FakeTimer implements Timer { _nextCall = _time.clock.now().add(_duration); } - bool get isActive => _time._timers.contains(this); + bool get isActive => _time._hasTimer(this); cancel() => _time._cancelTimer(this); } diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index 047377f8b..291db91d0 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -22,18 +22,12 @@ import 'package:unittest/unittest.dart'; main() { group('FakeTime', () { - FakeTime unit; - DateTime initialTime; - Duration elapseBy; - - setUp(() { - initialTime = new DateTime(2000); - unit = new FakeTime(initialTime: initialTime); - elapseBy = const Duration(days: 1); - }); + var initialTime = new DateTime(2000); + var elapseBy = const Duration(days: 1); + unit() => new FakeTime(initialTime: initialTime); test('should set initial time', () { - expect(unit.clock.now(), initialTime); + expect(unit().clock.now(), initialTime); }); test('should default initial time to system time', () { @@ -47,19 +41,20 @@ main() { test('should elapse time without calling timers', () { var timerCalled = false; new Timer(elapseBy ~/ 2, () => timerCalled = true); - unit.elapseBlocking(elapseBy); + unit().elapseBlocking(elapseBy); expect(timerCalled, isFalse); }); test('should elapse time by the specified amount', () { - unit.elapseBlocking(elapseBy); - expect(unit.clock.now(), initialTime.add(elapseBy)); + var it = unit(); + it.elapseBlocking(elapseBy); + expect(it.clock.now(), initialTime.add(elapseBy)); }); test('should throw when called with a negative duration', () { expect(() { - unit.elapseBlocking(const Duration(days: -1)); + unit().elapseBlocking(const Duration(days: -1)); }, throwsA(new isInstanceOf())); }); @@ -68,21 +63,21 @@ main() { group('elapse', () { test('should elapse time by the specified amount', () { - unit.run((time) { + unit().run((time) { time.elapse(elapseBy); - expect(unit.clock.now(), initialTime.add(elapseBy)); + expect(time.clock.now(), initialTime.add(elapseBy)); }); }); test('should throw ArgumentError when called with a negative duration', () { expect( - () => unit.elapse(const Duration(days: -1)), + () => unit().elapse(const Duration(days: -1)), throwsA(new isInstanceOf())); }); test('should throw when called before previous call is complete', () { - unit.run((time) { + unit().run((time) { var error; new Timer(elapseBy ~/ 2, () { try { time.elapse(elapseBy); } @@ -98,7 +93,7 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - unit.run((time) { + unit().run((time) { var beforeCallCount = 0; var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); @@ -109,8 +104,19 @@ main() { }); }); + test('should call timers expiring due to elapseBlocking', () { + unit().run((time) { + bool secondaryCalled = false; + new Timer(elapseBy, () { time.elapseBlocking(elapseBy); }); + new Timer(elapseBy * 2, () { secondaryCalled = true; }); + time.elapse(elapseBy); + expect(secondaryCalled, isTrue); + expect(time.clock.now(), initialTime.add(elapseBy * 2)); + }); + }); + test('should call timers at their scheduled time', () { - unit.run((time) { + unit().run((time) { DateTime calledAt; var periodicCalledAt = []; new Timer(elapseBy ~/ 2, () {calledAt = time.clock.now();}); @@ -124,7 +130,7 @@ main() { }); test('should not call timers expiring after end time', () { - unit.run((time) { + unit().run((time) { var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); time.elapse(elapseBy); @@ -133,7 +139,7 @@ main() { }); test('should not call canceled timers', () { - unit.run((time) { + unit().run((time) { int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); @@ -143,7 +149,7 @@ main() { }); test('should call periodic timers each time the duration elapses', () { - unit.run((time) { + unit().run((time) { var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); time.elapse(elapseBy); @@ -151,8 +157,29 @@ main() { }); }); + test('should process microtasks surrounding each timer', () { + unit().run((time) { + var microtaskCalls = 0; + var timerCalls = 0; + scheduleMicrotasks() { + for(int i = 0; i < 5; i++) { + scheduleMicrotask(() => microtaskCalls++); + } + } + scheduleMicrotasks(); + new Timer.periodic(elapseBy ~/ 5, (_) { + timerCalls++; + expect(microtaskCalls, 5 * timerCalls); + scheduleMicrotasks(); + }); + time.elapse(elapseBy); + expect(timerCalls, 5); + expect(microtaskCalls, 5 * (timerCalls + 1)); + }); + }); + test('should pass the periodic timer itself to callbacks', () { - unit.run((time) { + unit().run((time) { Timer passedTimer; Timer periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); @@ -162,7 +189,7 @@ main() { }); test('should call microtasks before advancing time', () { - unit.run((time) { + unit().run((time) { DateTime calledAt; scheduleMicrotask((){ calledAt = time.clock.now(); }); time.elapse(const Duration(minutes: 1)); @@ -171,7 +198,7 @@ main() { }); test('should add event before advancing time', () { - return unit.run((time) { + return unit().run((time) { var controller = new StreamController(); var ret = controller.stream.first.then((_) { expect(time.clock.now(), initialTime); @@ -183,7 +210,7 @@ main() { }); test('should increase negative duration timers to zero duration', () { - unit.run((time) { + unit().run((time) { var negativeDuration = const Duration(days: -1); DateTime calledAt; new Timer(negativeDuration, () { calledAt = time.clock.now(); }); @@ -193,7 +220,7 @@ main() { }); test('should not be additive with elapseBlocking', () { - unit.run((time) { + unit().run((time) { new Timer(Duration.ZERO, () => time.elapseBlocking(elapseBy * 5)); time.elapse(elapseBy); expect(time.clock.now(), initialTime.add(elapseBy * 5)); @@ -203,7 +230,7 @@ main() { group('isActive', () { test('should be false after timer is run', () { - unit.run((time) { + unit().run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); time.elapse(elapseBy); expect(timer.isActive, isFalse); @@ -211,7 +238,7 @@ main() { }); test('should be true after periodic timer is run', () { - unit.run((time) { + unit().run((time) { var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); time.elapse(elapseBy); expect(timer.isActive, isTrue); @@ -219,7 +246,7 @@ main() { }); test('should be false after timer is canceled', () { - unit.run((time) { + unit().run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); expect(timer.isActive, isFalse); @@ -229,7 +256,7 @@ main() { }); test('should work with new Future()', () { - unit.run((time) { + unit().run((time) { var callCount = 0; new Future(() => callCount++); time.elapse(Duration.ZERO); @@ -238,7 +265,7 @@ main() { }); test('should work with Future.delayed', () { - unit.run((time) { + unit().run((time) { int result; new Future.delayed(elapseBy, () => result = 5); time.elapse(elapseBy); @@ -247,7 +274,7 @@ main() { }); test('should work with Future.timeout', () { - unit.run((time) { + unit().run((time) { var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); expect(timed, throwsA(new isInstanceOf())); @@ -258,8 +285,10 @@ main() { // TODO: Pausing and resuming the timeout Stream doesn't work since // it uses `new Stopwatch()`. + // + // See https://code.google.com/p/dart/issues/detail?id=18149 test('should work with Stream.periodic', () { - unit.run((time) { + unit().run((time) { var events = []; StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), @@ -273,7 +302,7 @@ main() { }); test('should work with Stream.timeout', () { - unit.run((time) { + unit().run((time) { var events = []; var errors = []; var controller = new StreamController(); From 559d1c0c14f81f761e629cafad40a3bb65e7fac1 Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Mon, 14 Apr 2014 13:46:52 -0500 Subject: [PATCH 018/113] Change clock to getClock(initialTime) initialTime is only relevant to the Clock, which is not always needed, so it should not be a constructor parameter. This allows initialTime to be a required argument, and thus not be named, and not having to default it to `new DateTime.now()` which introduces indeterminism to tests. --- pkgs/fake_async/lib/fake_time.dart | 51 +++++++------- pkgs/fake_async/test/fake_time_test.dart | 84 ++++++++++++------------ 2 files changed, 69 insertions(+), 66 deletions(-) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_time.dart index 0270ffde5..94a92e31e 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_time.dart @@ -31,22 +31,26 @@ part of quiver.testing.async; /// /// test('testedFunc', () { /// new FakeTime().run((time) { -/// testedFunc(clock: time.clock); +/// testedFunc(clock: time.getClock(initialTime)); /// time.elapse(duration); /// expect(...) /// }); /// }); abstract class FakeTime { - /// [initialTime] will be the time returned by [now] before any calls to - /// [elapse] or [elapseBlocking]. - factory FakeTime({DateTime initialTime}) = _FakeTime; + factory FakeTime() = _FakeTime; FakeTime._(); - /// Returns a fake [Clock] whose time elapses along with this Clock. Pass - /// this as a dependency to the unit under test. - Clock get clock; + /// 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. /// @@ -92,14 +96,15 @@ abstract class FakeTime { class _FakeTime extends FakeTime { - DateTime _now; - DateTime _elapsingTo; + Duration _elapsed = Duration.ZERO; + Duration _elapsingTo; - _FakeTime({DateTime initialTime}) : super._() { - _now = initialTime == null ? new DateTime.now() : initialTime; + _FakeTime() : super._() { + _elapsed; } - Clock get clock => new Clock(() => _now); + Clock getClock(DateTime initialTime) => + new Clock(() => initialTime.add(_elapsed)); void elapse(Duration duration) { if (duration.inMicroseconds < 0) { @@ -108,7 +113,7 @@ class _FakeTime extends FakeTime { if (_elapsingTo != null) { throw new StateError('Cannot elapse until previous elapse is complete.'); } - _elapsingTo = _now.add(duration); + _elapsingTo = _elapsed + duration; _drainMicrotasks(); Timer next; while ((next = _getNextTimer()) != null) { @@ -123,9 +128,9 @@ class _FakeTime extends FakeTime { if (duration.inMicroseconds < 0) { throw new ArgumentError('Cannot call elapse with negative duration'); } - _now = _now.add(duration); - if (_elapsingTo != null && _now.isAfter(_elapsingTo)) { - _elapsingTo = _now; + _elapsed += duration; + if (_elapsingTo != null && _elapsed > _elapsingTo) { + _elapsingTo = _elapsed; } } @@ -162,9 +167,9 @@ class _FakeTime extends FakeTime { _microtasks.add(microtask); }); - _elapseTo(DateTime to) { - if (to.millisecondsSinceEpoch > _now.millisecondsSinceEpoch) { - _now = to; + _elapseTo(Duration to) { + if (to > _elapsed) { + _elapsed = to; } } @@ -180,7 +185,7 @@ class _FakeTime extends FakeTime { } _FakeTimer _getNextTimer() { - return min(_timers.where((timer) => !timer._nextCall.isAfter(_elapsingTo)), + return min(_timers.where((timer) => timer._nextCall <= _elapsingTo), (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } @@ -189,7 +194,7 @@ class _FakeTime extends FakeTime { _elapseTo(timer._nextCall); if (timer._isPeriodic) { timer._callback(timer); - timer._nextCall = timer._nextCall.add(timer._duration); + timer._nextCall += timer._duration; } else { timer._callback(); _timers.remove(timer); @@ -214,7 +219,7 @@ class _FakeTimer implements Timer { final Function _callback; final bool _isPeriodic; final _FakeTime _time; - DateTime _nextCall; + Duration _nextCall; // TODO: In browser JavaScript, timers can only run every 4 milliseconds once // sufficiently nested: @@ -225,7 +230,7 @@ class _FakeTimer implements Timer { _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time) : _duration = duration < _minDuration ? _minDuration : duration { - _nextCall = _time.clock.now().add(_duration); + _nextCall = _time._elapsed + _duration; } bool get isActive => _time._hasTimer(this); diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_time_test.dart index 291db91d0..52f381a6c 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_time_test.dart @@ -24,16 +24,9 @@ main() { var initialTime = new DateTime(2000); var elapseBy = const Duration(days: 1); - unit() => new FakeTime(initialTime: initialTime); test('should set initial time', () { - expect(unit().clock.now(), initialTime); - }); - - test('should default initial time to system time', () { - expect( - new FakeTime().clock.now().millisecondsSinceEpoch, - closeTo(new DateTime.now().millisecondsSinceEpoch, 500)); + expect(new FakeTime().getClock(initialTime).now(), initialTime); }); group('elapseBlocking', () { @@ -41,20 +34,20 @@ main() { test('should elapse time without calling timers', () { var timerCalled = false; new Timer(elapseBy ~/ 2, () => timerCalled = true); - unit().elapseBlocking(elapseBy); + new FakeTime().elapseBlocking(elapseBy); expect(timerCalled, isFalse); }); test('should elapse time by the specified amount', () { - var it = unit(); + var it = new FakeTime(); it.elapseBlocking(elapseBy); - expect(it.clock.now(), initialTime.add(elapseBy)); + expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); }); test('should throw when called with a negative duration', () { expect(() { - unit().elapseBlocking(const Duration(days: -1)); + new FakeTime().elapseBlocking(const Duration(days: -1)); }, throwsA(new isInstanceOf())); }); @@ -63,21 +56,21 @@ main() { group('elapse', () { test('should elapse time by the specified amount', () { - unit().run((time) { + new FakeTime().run((time) { time.elapse(elapseBy); - expect(time.clock.now(), initialTime.add(elapseBy)); + expect(time.getClock(initialTime).now(), initialTime.add(elapseBy)); }); }); test('should throw ArgumentError when called with a negative duration', () { expect( - () => unit().elapse(const Duration(days: -1)), + () => new FakeTime().elapse(const Duration(days: -1)), throwsA(new isInstanceOf())); }); test('should throw when called before previous call is complete', () { - unit().run((time) { + new FakeTime().run((time) { var error; new Timer(elapseBy ~/ 2, () { try { time.elapse(elapseBy); } @@ -93,7 +86,7 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - unit().run((time) { + new FakeTime().run((time) { var beforeCallCount = 0; var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); @@ -105,23 +98,23 @@ main() { }); test('should call timers expiring due to elapseBlocking', () { - unit().run((time) { + new FakeTime().run((time) { bool secondaryCalled = false; new Timer(elapseBy, () { time.elapseBlocking(elapseBy); }); new Timer(elapseBy * 2, () { secondaryCalled = true; }); time.elapse(elapseBy); expect(secondaryCalled, isTrue); - expect(time.clock.now(), initialTime.add(elapseBy * 2)); + expect(time.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); }); }); test('should call timers at their scheduled time', () { - unit().run((time) { + new FakeTime().run((time) { DateTime calledAt; var periodicCalledAt = []; - new Timer(elapseBy ~/ 2, () {calledAt = time.clock.now();}); + new Timer(elapseBy ~/ 2, () {calledAt = time.getClock(initialTime).now();}); new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(time.clock.now());}); + periodicCalledAt.add(time.getClock(initialTime).now());}); time.elapse(elapseBy); expect(calledAt, initialTime.add(elapseBy ~/ 2)); expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] @@ -130,7 +123,7 @@ main() { }); test('should not call timers expiring after end time', () { - unit().run((time) { + new FakeTime().run((time) { var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); time.elapse(elapseBy); @@ -139,7 +132,7 @@ main() { }); test('should not call canceled timers', () { - unit().run((time) { + new FakeTime().run((time) { int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); @@ -149,7 +142,7 @@ main() { }); test('should call periodic timers each time the duration elapses', () { - unit().run((time) { + new FakeTime().run((time) { var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); time.elapse(elapseBy); @@ -158,7 +151,7 @@ main() { }); test('should process microtasks surrounding each timer', () { - unit().run((time) { + new FakeTime().run((time) { var microtaskCalls = 0; var timerCalls = 0; scheduleMicrotasks() { @@ -179,7 +172,7 @@ main() { }); test('should pass the periodic timer itself to callbacks', () { - unit().run((time) { + new FakeTime().run((time) { Timer passedTimer; Timer periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); @@ -189,19 +182,21 @@ main() { }); test('should call microtasks before advancing time', () { - unit().run((time) { + new FakeTime().run((time) { DateTime calledAt; - scheduleMicrotask((){ calledAt = time.clock.now(); }); + scheduleMicrotask((){ + calledAt = time.getClock(initialTime).now(); + }); time.elapse(const Duration(minutes: 1)); expect(calledAt, initialTime); }); }); test('should add event before advancing time', () { - return unit().run((time) { + return new FakeTime().run((time) { var controller = new StreamController(); var ret = controller.stream.first.then((_) { - expect(time.clock.now(), initialTime); + expect(time.getClock(initialTime).now(), initialTime); }); controller.add(null); time.elapse(const Duration(minutes: 1)); @@ -210,27 +205,30 @@ main() { }); test('should increase negative duration timers to zero duration', () { - unit().run((time) { + new FakeTime().run((time) { var negativeDuration = const Duration(days: -1); DateTime calledAt; - new Timer(negativeDuration, () { calledAt = time.clock.now(); }); + new Timer(negativeDuration, () { + calledAt = time.getClock(initialTime).now(); + }); time.elapse(const Duration(minutes: 1)); expect(calledAt, initialTime); }); }); test('should not be additive with elapseBlocking', () { - unit().run((time) { + new FakeTime().run((time) { new Timer(Duration.ZERO, () => time.elapseBlocking(elapseBy * 5)); time.elapse(elapseBy); - expect(time.clock.now(), initialTime.add(elapseBy * 5)); + expect(time.getClock(initialTime).now(), + initialTime.add(elapseBy * 5)); }); }); group('isActive', () { test('should be false after timer is run', () { - unit().run((time) { + new FakeTime().run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); time.elapse(elapseBy); expect(timer.isActive, isFalse); @@ -238,7 +236,7 @@ main() { }); test('should be true after periodic timer is run', () { - unit().run((time) { + new FakeTime().run((time) { var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); time.elapse(elapseBy); expect(timer.isActive, isTrue); @@ -246,7 +244,7 @@ main() { }); test('should be false after timer is canceled', () { - unit().run((time) { + new FakeTime().run((time) { var timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); expect(timer.isActive, isFalse); @@ -256,7 +254,7 @@ main() { }); test('should work with new Future()', () { - unit().run((time) { + new FakeTime().run((time) { var callCount = 0; new Future(() => callCount++); time.elapse(Duration.ZERO); @@ -265,7 +263,7 @@ main() { }); test('should work with Future.delayed', () { - unit().run((time) { + new FakeTime().run((time) { int result; new Future.delayed(elapseBy, () => result = 5); time.elapse(elapseBy); @@ -274,7 +272,7 @@ main() { }); test('should work with Future.timeout', () { - unit().run((time) { + new FakeTime().run((time) { var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); expect(timed, throwsA(new isInstanceOf())); @@ -288,7 +286,7 @@ main() { // // See https://code.google.com/p/dart/issues/detail?id=18149 test('should work with Stream.periodic', () { - unit().run((time) { + new FakeTime().run((time) { var events = []; StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), @@ -302,7 +300,7 @@ main() { }); test('should work with Stream.timeout', () { - unit().run((time) { + new FakeTime().run((time) { var events = []; var errors = []; var controller = new StreamController(); From 6ae857133c6dbc51d6b80be7dd0f195e2749d12a Mon Sep 17 00:00:00 2001 From: Sean Eagan Date: Wed, 30 Apr 2014 09:29:46 -0500 Subject: [PATCH 019/113] Rename to FakeAsync --- .../lib/{fake_time.dart => fake_async.dart} | 22 ++-- ...ke_time_test.dart => fake_async_test.dart} | 124 +++++++++--------- 2 files changed, 73 insertions(+), 73 deletions(-) rename pkgs/fake_async/lib/{fake_time.dart => fake_async.dart} (92%) rename pkgs/fake_async/test/{fake_time_test.dart => fake_async_test.dart} (71%) diff --git a/pkgs/fake_async/lib/fake_time.dart b/pkgs/fake_async/lib/fake_async.dart similarity index 92% rename from pkgs/fake_async/lib/fake_time.dart rename to pkgs/fake_async/lib/fake_async.dart index 94a92e31e..246f541f4 100644 --- a/pkgs/fake_async/lib/fake_time.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -30,17 +30,17 @@ part of quiver.testing.async; /// Example: /// /// test('testedFunc', () { -/// new FakeTime().run((time) { -/// testedFunc(clock: time.getClock(initialTime)); -/// time.elapse(duration); +/// new FakeAsync().run((async) { +/// testedFunc(clock: async.getClock(initialTime)); +/// async.elapse(duration); /// expect(...) /// }); /// }); -abstract class FakeTime { +abstract class FakeAsync { - factory FakeTime() = _FakeTime; + factory FakeAsync() = _FakeAsync; - FakeTime._(); + FakeAsync._(); /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and /// [elapseBlocking]. @@ -91,15 +91,15 @@ abstract class FakeTime { /// execution within the zone via calls to [elapse]. /// /// [callback] is called with `this` as argument. - run(callback(FakeTime self)); + run(callback(FakeAsync self)); } -class _FakeTime extends FakeTime { +class _FakeAsync extends FakeAsync { Duration _elapsed = Duration.ZERO; Duration _elapsingTo; - _FakeTime() : super._() { + _FakeAsync() : super._() { _elapsed; } @@ -134,7 +134,7 @@ class _FakeTime extends FakeTime { } } - run(callback(FakeTime self)) { + run(callback(FakeAsync self)) { if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); } @@ -218,7 +218,7 @@ class _FakeTimer implements Timer { final Duration _duration; final Function _callback; final bool _isPeriodic; - final _FakeTime _time; + final _FakeAsync _time; Duration _nextCall; // TODO: In browser JavaScript, timers can only run every 4 milliseconds once diff --git a/pkgs/fake_async/test/fake_time_test.dart b/pkgs/fake_async/test/fake_async_test.dart similarity index 71% rename from pkgs/fake_async/test/fake_time_test.dart rename to pkgs/fake_async/test/fake_async_test.dart index 52f381a6c..e379a98bb 100644 --- a/pkgs/fake_async/test/fake_time_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -library quiver.testing.async.fake_time_test; +library quiver.testing.async.fake_async_test; import 'dart:async'; @@ -20,13 +20,13 @@ import 'package:quiver/testing/async.dart'; import 'package:unittest/unittest.dart'; main() { - group('FakeTime', () { + group('FakeAsync', () { var initialTime = new DateTime(2000); var elapseBy = const Duration(days: 1); test('should set initial time', () { - expect(new FakeTime().getClock(initialTime).now(), initialTime); + expect(new FakeAsync().getClock(initialTime).now(), initialTime); }); group('elapseBlocking', () { @@ -34,12 +34,12 @@ main() { test('should elapse time without calling timers', () { var timerCalled = false; new Timer(elapseBy ~/ 2, () => timerCalled = true); - new FakeTime().elapseBlocking(elapseBy); + new FakeAsync().elapseBlocking(elapseBy); expect(timerCalled, isFalse); }); test('should elapse time by the specified amount', () { - var it = new FakeTime(); + var it = new FakeAsync(); it.elapseBlocking(elapseBy); expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); }); @@ -47,7 +47,7 @@ main() { test('should throw when called with a negative duration', () { expect(() { - new FakeTime().elapseBlocking(const Duration(days: -1)); + new FakeAsync().elapseBlocking(const Duration(days: -1)); }, throwsA(new isInstanceOf())); }); @@ -56,29 +56,29 @@ main() { group('elapse', () { test('should elapse time by the specified amount', () { - new FakeTime().run((time) { - time.elapse(elapseBy); - expect(time.getClock(initialTime).now(), initialTime.add(elapseBy)); + new FakeAsync().run((async) { + async.elapse(elapseBy); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); }); }); test('should throw ArgumentError when called with a negative duration', () { expect( - () => new FakeTime().elapse(const Duration(days: -1)), + () => new FakeAsync().elapse(const Duration(days: -1)), throwsA(new isInstanceOf())); }); test('should throw when called before previous call is complete', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var error; new Timer(elapseBy ~/ 2, () { - try { time.elapse(elapseBy); } + try { async.elapse(elapseBy); } catch (e) { error = e; } }); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(error, new isInstanceOf()); }); }); @@ -86,36 +86,36 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var beforeCallCount = 0; var atCallCount = 0; new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); new Timer(elapseBy, () {atCallCount++;}); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(beforeCallCount, 1); expect(atCallCount, 1); }); }); test('should call timers expiring due to elapseBlocking', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { bool secondaryCalled = false; - new Timer(elapseBy, () { time.elapseBlocking(elapseBy); }); + new Timer(elapseBy, () { async.elapseBlocking(elapseBy); }); new Timer(elapseBy * 2, () { secondaryCalled = true; }); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(secondaryCalled, isTrue); - expect(time.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); }); }); test('should call timers at their scheduled time', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { DateTime calledAt; var periodicCalledAt = []; - new Timer(elapseBy ~/ 2, () {calledAt = time.getClock(initialTime).now();}); + new Timer(elapseBy ~/ 2, () {calledAt = async.getClock(initialTime).now();}); new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(time.getClock(initialTime).now());}); - time.elapse(elapseBy); + periodicCalledAt.add(async.getClock(initialTime).now());}); + async.elapse(elapseBy); expect(calledAt, initialTime.add(elapseBy ~/ 2)); expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] .map(initialTime.add)); @@ -123,35 +123,35 @@ main() { }); test('should not call timers expiring after end time', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var timerCallCount = 0; new Timer(elapseBy * 2, () {timerCallCount++;}); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(timerCallCount, 0); }); }); test('should not call canceled timers', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { int timerCallCount = 0; var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); timer.cancel(); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(timerCallCount, 0); }); }); test('should call periodic timers each time the duration elapses', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var periodicCallCount = 0; new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(periodicCallCount, 10); }); }); test('should process microtasks surrounding each timer', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var microtaskCalls = 0; var timerCalls = 0; scheduleMicrotasks() { @@ -165,62 +165,62 @@ main() { expect(microtaskCalls, 5 * timerCalls); scheduleMicrotasks(); }); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(timerCalls, 5); expect(microtaskCalls, 5 * (timerCalls + 1)); }); }); test('should pass the periodic timer itself to callbacks', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { Timer passedTimer; Timer periodic = new Timer.periodic(elapseBy, (timer) {passedTimer = timer;}); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(periodic, same(passedTimer)); }); }); test('should call microtasks before advancing time', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { DateTime calledAt; scheduleMicrotask((){ - calledAt = time.getClock(initialTime).now(); + calledAt = async.getClock(initialTime).now(); }); - time.elapse(const Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); expect(calledAt, initialTime); }); }); test('should add event before advancing time', () { - return new FakeTime().run((time) { + return new FakeAsync().run((async) { var controller = new StreamController(); var ret = controller.stream.first.then((_) { - expect(time.getClock(initialTime).now(), initialTime); + expect(async.getClock(initialTime).now(), initialTime); }); controller.add(null); - time.elapse(const Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); return ret; }); }); test('should increase negative duration timers to zero duration', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var negativeDuration = const Duration(days: -1); DateTime calledAt; new Timer(negativeDuration, () { - calledAt = time.getClock(initialTime).now(); + calledAt = async.getClock(initialTime).now(); }); - time.elapse(const Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); expect(calledAt, initialTime); }); }); test('should not be additive with elapseBlocking', () { - new FakeTime().run((time) { - new Timer(Duration.ZERO, () => time.elapseBlocking(elapseBy * 5)); - time.elapse(elapseBy); - expect(time.getClock(initialTime).now(), + new FakeAsync().run((async) { + new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); + async.elapse(elapseBy); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy * 5)); }); }); @@ -228,23 +228,23 @@ main() { group('isActive', () { test('should be false after timer is run', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var timer = new Timer(elapseBy ~/ 2, () {}); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(timer.isActive, isFalse); }); }); test('should be true after periodic timer is run', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(timer.isActive, isTrue); }); }); test('should be false after timer is canceled', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var timer = new Timer(elapseBy ~/ 2, () {}); timer.cancel(); expect(timer.isActive, isFalse); @@ -254,29 +254,29 @@ main() { }); test('should work with new Future()', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var callCount = 0; new Future(() => callCount++); - time.elapse(Duration.ZERO); + async.elapse(Duration.ZERO); expect(callCount, 1); }); }); test('should work with Future.delayed', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { int result; new Future.delayed(elapseBy, () => result = 5); - time.elapse(elapseBy); + async.elapse(elapseBy); expect(result, 5); }); }); test('should work with Future.timeout', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var completer = new Completer(); var timed = completer.future.timeout(elapseBy ~/ 2); expect(timed, throwsA(new isInstanceOf())); - time.elapse(elapseBy); + async.elapse(elapseBy); completer.complete(); }); }); @@ -286,13 +286,13 @@ main() { // // See https://code.google.com/p/dart/issues/detail?id=18149 test('should work with Stream.periodic', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var events = []; StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); subscription = periodic.listen(events.add, cancelOnError: true); - time.elapse(const Duration(minutes: 3)); + async.elapse(const Duration(minutes: 3)); subscription.cancel(); expect(events, [0, 1, 2]); }); @@ -300,7 +300,7 @@ main() { }); test('should work with Stream.timeout', () { - new FakeTime().run((time) { + new FakeAsync().run((async) { var events = []; var errors = []; var controller = new StreamController(); @@ -308,9 +308,9 @@ main() { var subscription = timed.listen(events.add, onError: errors.add, cancelOnError: true); controller.add(0); - time.elapse(const Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); expect(events, [0]); - time.elapse(const Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); subscription.cancel(); expect(errors, hasLength(1)); expect(errors.first, new isInstanceOf()); From 3f15ca92196905103bc0f8e9272d4330fb4c2fb8 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 11 Jun 2014 09:47:20 -0700 Subject: [PATCH 020/113] Remove SYSTEM_CLOCK. Use const Clock() instead. --- pkgs/fake_async/lib/fake_async.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 246f541f4..302949c71 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -24,7 +24,7 @@ part of quiver.testing.async; /// simulated using [elapseBlocking]. /// /// To allow the unit under test to tell time, it can receive a [Clock] as a -/// dependency, and default it to [SYSTEM_CLOCK] in production, but then use +/// dependency, and default it to [const Clock()] in production, but then use /// [clock] in test code. /// /// Example: From 336be2395934bd60134c0043fddfa77c037fddaa Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 26 Aug 2014 11:48:25 -0700 Subject: [PATCH 021/113] Wrap returned Future to ensure any then/whenComplete/etc. called by unittest are not scheduled in FakeAsync's custom zone. --- pkgs/fake_async/test/fake_async_test.dart | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index e379a98bb..c1fda6590 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -193,7 +193,7 @@ main() { }); test('should add event before advancing time', () { - return new FakeAsync().run((async) { + return new Future(() => new FakeAsync().run((async) { var controller = new StreamController(); var ret = controller.stream.first.then((_) { expect(async.getClock(initialTime).now(), initialTime); @@ -201,7 +201,7 @@ main() { controller.add(null); async.elapse(const Duration(minutes: 1)); return ret; - }); + })); }); test('should increase negative duration timers to zero duration', () { @@ -316,13 +316,8 @@ main() { expect(errors.first, new isInstanceOf()); return controller.close(); }); - }); - }); - }); - }); - } From 8d004e37d8461165c2d2a5188833414edb27772b Mon Sep 17 00:00:00 2001 From: yjbanov Date: Tue, 9 Sep 2014 16:01:59 -0700 Subject: [PATCH 022/113] flushMicrotasks and flushTimers --- pkgs/fake_async/lib/fake_async.dart | 63 ++++++++-- pkgs/fake_async/test/fake_async_test.dart | 141 ++++++++++++++++++++++ 2 files changed, 192 insertions(+), 12 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 302949c71..9d93c55cf 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -92,12 +92,30 @@ abstract class FakeAsync { /// /// [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. + void flushTimers({Duration timeout: const Duration(hours: 1), + bool flushPeriodicTimers: true}); } class _FakeAsync extends FakeAsync { Duration _elapsed = Duration.ZERO; Duration _elapsingTo; + Queue _microtasks = new Queue(); + Set<_FakeTimer> _timers = new Set<_FakeTimer>(); + bool _waitingForTimer = false; _FakeAsync() : super._() { _elapsed; @@ -114,12 +132,7 @@ class _FakeAsync extends FakeAsync { throw new StateError('Cannot elapse until previous elapse is complete.'); } _elapsingTo = _elapsed + duration; - _drainMicrotasks(); - Timer next; - while ((next = _getNextTimer()) != null) { - _runTimer(next); - _drainMicrotasks(); - } + _drainTimersWhile((_FakeTimer next) => next._nextCall <= _elapsingTo); _elapseTo(_elapsingTo); _elapsingTo = null; } @@ -134,6 +147,28 @@ class _FakeAsync extends FakeAsync { } } + @override + void flushMicrotasks() { + _drainMicrotasks(); + } + + @override + void flushTimers({Duration timeout: const Duration(hours: 1), + bool flushPeriodicTimers: true}) { + _drainTimersWhile((_FakeTimer timer) { + if (timer._nextCall > timeout) { + 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); + } + }); + } + run(callback(FakeAsync self)) { if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); @@ -167,17 +202,21 @@ class _FakeAsync extends FakeAsync { _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; } } - Queue _microtasks = new Queue(); - - Set<_FakeTimer> _timers = new Set<_FakeTimer>(); - bool _waitingForTimer = false; - Timer _createTimer(Duration duration, Function callback, bool isPeriodic) { var timer = new _FakeTimer._(duration, callback, isPeriodic, this); _timers.add(timer); @@ -185,7 +224,7 @@ class _FakeAsync extends FakeAsync { } _FakeTimer _getNextTimer() { - return min(_timers.where((timer) => timer._nextCall <= _elapsingTo), + return min(_timers, (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); } diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index c1fda6590..c9475b91e 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -319,5 +319,146 @@ main() { }); }); }); + + group('flushMicrotasks', () { + test('should flush a microtask', () { + new FakeAsync().run((async) { + bool microtaskRan = false; + new Future.microtask(() { + microtaskRan = true; + }); + expect(microtaskRan, isFalse, + reason: 'should not flush until asked to'); + async.flushMicrotasks(); + expect(microtaskRan, isTrue); + }); + }); + test('should flush microtasks scheduled by microtasks in order', () { + new FakeAsync().run((async) { + final log = []; + new Future.microtask(() { + log.add(1); + new Future.microtask(() { + log.add(3); + }); + }); + new Future.microtask(() { + log.add(2); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushMicrotasks(); + expect(log, [1, 2, 3]); + }); + }); + test('should not run timers', () { + new FakeAsync().run((async) { + final log = []; + new Future.microtask(() { + log.add(1); + }); + new Future(() { + log.add(2); + }); + new Timer.periodic(new Duration(seconds: 1), (_) { + log.add(2); + }); + async.flushMicrotasks(); + expect(log, [1]); + }); + }); + }); + + group('flushTimers', () { + test('should flush timers', () { + new FakeAsync().run((async) { + final log = []; + new Future(() { + log.add(2); + new Future.delayed(elapseBy, () { + log.add(3); + }); + }); + new Future(() { + log.add(1); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushTimers(timeout: elapseBy * 2, flushPeriodicTimers: false); + expect(log, [1, 2, 3]); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + }); + }); + + test('should run collateral periodic timers', () { + new FakeAsync().run((async) { + final log = []; + new Future.delayed(new Duration(seconds: 2), () { + log.add('delayed'); + }); + new Timer.periodic(new Duration(seconds: 1), (_) { + log.add('periodic'); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushTimers(flushPeriodicTimers: false); + expect(log, ['periodic', 'periodic', 'delayed']); + }); + }); + + test('should timeout', () { + new FakeAsync().run((async) { + int count = 0; + // Schedule 3 timers. All but the last one should fire. + for (int delay in [30, 60, 90]) { + new Future.delayed(new Duration(minutes: delay), () { + count++; + }); + } + expect(() => async.flushTimers(flushPeriodicTimers: false), + throwsStateError); + expect(count, 2); + }); + }); + + test('should timeout a chain of timers', () { + new FakeAsync().run((async) { + int count = 0; + createTimer() { + new Future.delayed(new Duration(minutes: 30), () { + count++; + createTimer(); + }); + } + createTimer(); + expect(() => async.flushTimers(timeout: new Duration(hours: 2), + flushPeriodicTimers: false), throwsStateError); + expect(count, 4); + }); + }); + + test('should timeout periodic timers', () { + new FakeAsync().run((async) { + int count = 0; + new Timer.periodic(new Duration(minutes: 30), (Timer timer) { + count++; + }); + expect(() => async.flushTimers(timeout: new Duration(hours: 1)), + throwsStateError); + expect(count, 2); + }); + }); + + test('should flush periodic timers', () { + new FakeAsync().run((async) { + int count = 0; + new Timer.periodic(new Duration(minutes: 30), (Timer timer) { + if (count == 3) { + timer.cancel(); + } + count++; + }); + async.flushTimers(timeout: new Duration(hours: 20)); + expect(count, 4); + }); + }); + }); }); } From cac0f7a44ca8144984e24975d6a496453eef4d99 Mon Sep 17 00:00:00 2001 From: yjbanov Date: Tue, 7 Oct 2014 10:40:54 -0700 Subject: [PATCH 023/113] make flushTimers timeout relative to elapsed time --- pkgs/fake_async/lib/fake_async.dart | 5 +++-- pkgs/fake_async/test/fake_async_test.dart | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 9d93c55cf..ec93a6fb4 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -104,7 +104,7 @@ abstract class FakeAsync { /// /// [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. + /// is 1 hour. [timeout] is relative to the elapsed time. void flushTimers({Duration timeout: const Duration(hours: 1), bool flushPeriodicTimers: true}); } @@ -155,8 +155,9 @@ class _FakeAsync extends FakeAsync { @override void flushTimers({Duration timeout: const Duration(hours: 1), bool flushPeriodicTimers: true}) { + final absoluteTimeout = _elapsed + timeout; _drainTimersWhile((_FakeTimer timer) { - if (timer._nextCall > timeout) { + if (timer._nextCall > absoluteTimeout) { throw new StateError( 'Exceeded timeout ${timeout} while flushing timers'); } diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index c9475b91e..06fb8af4b 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -459,6 +459,26 @@ main() { expect(count, 4); }); }); + + test('should compute absolute timeout as elapsed + timeout', () { + new FakeAsync().run((async) { + final log = []; + int count = 0; + createTimer() { + new Future.delayed(new Duration(minutes: 30), () { + log.add(count); + count++; + if (count < 4) { + createTimer(); + } + }); + } + createTimer(); + async.elapse(new Duration(hours: 1)); + async.flushTimers(timeout: new Duration(hours: 1)); + expect(count, 4); + }); + }); }); }); } From b1aaa6da371d65b9ea7c56c2618ce9ee53f4f110 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 21 Jan 2015 14:58:25 -0800 Subject: [PATCH 024/113] Removed a number of unused fields, fixed analyzer warning for join --- pkgs/fake_async/lib/fake_async.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index ec93a6fb4..77f10b331 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -115,7 +115,6 @@ class _FakeAsync extends FakeAsync { Duration _elapsingTo; Queue _microtasks = new Queue(); Set<_FakeTimer> _timers = new Set<_FakeTimer>(); - bool _waitingForTimer = false; _FakeAsync() : super._() { _elapsed; From c9490a1e9473348882b25d1a60c9e20d5fb1d99b Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 30 Jan 2015 19:28:08 -0800 Subject: [PATCH 025/113] Force normalization to UNIX (LF) line-endings. --- pkgs/fake_async/lib/fake_async.dart | 556 ++++++------- pkgs/fake_async/test/fake_async_test.dart | 968 +++++++++++----------- 2 files changed, 762 insertions(+), 762 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 77f10b331..736072a0d 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -1,278 +1,278 @@ -// 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}); -} - -class _FakeAsync extends FakeAsync { - - Duration _elapsed = Duration.ZERO; - Duration _elapsingTo; - Queue _microtasks = new Queue(); - Set<_FakeTimer> _timers = new Set<_FakeTimer>(); - - _FakeAsync() : super._() { - _elapsed; - } - - Clock getClock(DateTime initialTime) => - new Clock(() => initialTime.add(_elapsed)); - - 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; - } - - 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); - } - }); - } - - run(callback(FakeAsync self)) { - if (_zone == null) { - _zone = Zone.current.fork(specification: _zoneSpec); - } - return _zone.runGuarded(() => callback(this)); - } - Zone _zone; - - 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); -} +// 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}); +} + +class _FakeAsync extends FakeAsync { + + Duration _elapsed = Duration.ZERO; + Duration _elapsingTo; + Queue _microtasks = new Queue(); + Set<_FakeTimer> _timers = new Set<_FakeTimer>(); + + _FakeAsync() : super._() { + _elapsed; + } + + Clock getClock(DateTime initialTime) => + new Clock(() => initialTime.add(_elapsed)); + + 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; + } + + 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); + } + }); + } + + run(callback(FakeAsync self)) { + if (_zone == null) { + _zone = Zone.current.fork(specification: _zoneSpec); + } + return _zone.runGuarded(() => callback(this)); + } + Zone _zone; + + 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); +} diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 06fb8af4b..e44b9fe7a 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -1,484 +1,484 @@ -// 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. - -library quiver.testing.async.fake_async_test; - -import 'dart:async'; - -import 'package:quiver/testing/async.dart'; -import 'package:unittest/unittest.dart'; - -main() { - group('FakeAsync', () { - - var initialTime = new DateTime(2000); - var elapseBy = const Duration(days: 1); - - test('should set initial time', () { - expect(new FakeAsync().getClock(initialTime).now(), initialTime); - }); - - group('elapseBlocking', () { - - test('should elapse time without calling timers', () { - var timerCalled = false; - new Timer(elapseBy ~/ 2, () => timerCalled = true); - new FakeAsync().elapseBlocking(elapseBy); - expect(timerCalled, isFalse); - }); - - test('should elapse time by the specified amount', () { - var it = new FakeAsync(); - it.elapseBlocking(elapseBy); - expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); - }); - - test('should throw when called with a negative duration', - () { - expect(() { - new FakeAsync().elapseBlocking(const Duration(days: -1)); - }, throwsA(new isInstanceOf())); - }); - - }); - - group('elapse', () { - - test('should elapse time by the specified amount', () { - new FakeAsync().run((async) { - async.elapse(elapseBy); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); - }); - }); - - test('should throw ArgumentError when called with a negative duration', - () { - expect( - () => new FakeAsync().elapse(const Duration(days: -1)), - throwsA(new isInstanceOf())); - }); - - test('should throw when called before previous call is complete', () { - new FakeAsync().run((async) { - var error; - new Timer(elapseBy ~/ 2, () { - try { async.elapse(elapseBy); } - catch (e) { - error = e; - } - }); - async.elapse(elapseBy); - expect(error, new isInstanceOf()); - }); - }); - - group('when creating timers', () { - - test('should call timers expiring before or at end time', () { - new FakeAsync().run((async) { - var beforeCallCount = 0; - var atCallCount = 0; - new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); - new Timer(elapseBy, () {atCallCount++;}); - async.elapse(elapseBy); - expect(beforeCallCount, 1); - expect(atCallCount, 1); - }); - }); - - test('should call timers expiring due to elapseBlocking', () { - new FakeAsync().run((async) { - bool secondaryCalled = false; - new Timer(elapseBy, () { async.elapseBlocking(elapseBy); }); - new Timer(elapseBy * 2, () { secondaryCalled = true; }); - async.elapse(elapseBy); - expect(secondaryCalled, isTrue); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); - }); - }); - - test('should call timers at their scheduled time', () { - new FakeAsync().run((async) { - DateTime calledAt; - var periodicCalledAt = []; - new Timer(elapseBy ~/ 2, () {calledAt = async.getClock(initialTime).now();}); - new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(async.getClock(initialTime).now());}); - async.elapse(elapseBy); - expect(calledAt, initialTime.add(elapseBy ~/ 2)); - expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] - .map(initialTime.add)); - }); - }); - - test('should not call timers expiring after end time', () { - new FakeAsync().run((async) { - var timerCallCount = 0; - new Timer(elapseBy * 2, () {timerCallCount++;}); - async.elapse(elapseBy); - expect(timerCallCount, 0); - }); - }); - - test('should not call canceled timers', () { - new FakeAsync().run((async) { - int timerCallCount = 0; - var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); - timer.cancel(); - async.elapse(elapseBy); - expect(timerCallCount, 0); - }); - }); - - test('should call periodic timers each time the duration elapses', () { - new FakeAsync().run((async) { - var periodicCallCount = 0; - new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); - async.elapse(elapseBy); - expect(periodicCallCount, 10); - }); - }); - - test('should process microtasks surrounding each timer', () { - new FakeAsync().run((async) { - var microtaskCalls = 0; - var timerCalls = 0; - scheduleMicrotasks() { - for(int i = 0; i < 5; i++) { - scheduleMicrotask(() => microtaskCalls++); - } - } - scheduleMicrotasks(); - new Timer.periodic(elapseBy ~/ 5, (_) { - timerCalls++; - expect(microtaskCalls, 5 * timerCalls); - scheduleMicrotasks(); - }); - async.elapse(elapseBy); - expect(timerCalls, 5); - expect(microtaskCalls, 5 * (timerCalls + 1)); - }); - }); - - test('should pass the periodic timer itself to callbacks', () { - new FakeAsync().run((async) { - Timer passedTimer; - Timer periodic = new Timer.periodic(elapseBy, - (timer) {passedTimer = timer;}); - async.elapse(elapseBy); - expect(periodic, same(passedTimer)); - }); - }); - - test('should call microtasks before advancing time', () { - new FakeAsync().run((async) { - DateTime calledAt; - scheduleMicrotask((){ - calledAt = async.getClock(initialTime).now(); - }); - async.elapse(const Duration(minutes: 1)); - expect(calledAt, initialTime); - }); - }); - - test('should add event before advancing time', () { - return new Future(() => new FakeAsync().run((async) { - var controller = new StreamController(); - var ret = controller.stream.first.then((_) { - expect(async.getClock(initialTime).now(), initialTime); - }); - controller.add(null); - async.elapse(const Duration(minutes: 1)); - return ret; - })); - }); - - test('should increase negative duration timers to zero duration', () { - new FakeAsync().run((async) { - var negativeDuration = const Duration(days: -1); - DateTime calledAt; - new Timer(negativeDuration, () { - calledAt = async.getClock(initialTime).now(); - }); - async.elapse(const Duration(minutes: 1)); - expect(calledAt, initialTime); - }); - }); - - test('should not be additive with elapseBlocking', () { - new FakeAsync().run((async) { - new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); - async.elapse(elapseBy); - expect(async.getClock(initialTime).now(), - initialTime.add(elapseBy * 5)); - }); - }); - - group('isActive', () { - - test('should be false after timer is run', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, () {}); - async.elapse(elapseBy); - expect(timer.isActive, isFalse); - }); - }); - - test('should be true after periodic timer is run', () { - new FakeAsync().run((async) { - var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); - async.elapse(elapseBy); - expect(timer.isActive, isTrue); - }); - }); - - test('should be false after timer is canceled', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, () {}); - timer.cancel(); - expect(timer.isActive, isFalse); - }); - }); - - }); - - test('should work with new Future()', () { - new FakeAsync().run((async) { - var callCount = 0; - new Future(() => callCount++); - async.elapse(Duration.ZERO); - expect(callCount, 1); - }); - }); - - test('should work with Future.delayed', () { - new FakeAsync().run((async) { - int result; - new Future.delayed(elapseBy, () => result = 5); - async.elapse(elapseBy); - expect(result, 5); - }); - }); - - test('should work with Future.timeout', () { - new FakeAsync().run((async) { - var completer = new Completer(); - var timed = completer.future.timeout(elapseBy ~/ 2); - expect(timed, throwsA(new isInstanceOf())); - async.elapse(elapseBy); - completer.complete(); - }); - }); - - // TODO: Pausing and resuming the timeout Stream doesn't work since - // it uses `new Stopwatch()`. - // - // See https://code.google.com/p/dart/issues/detail?id=18149 - test('should work with Stream.periodic', () { - new FakeAsync().run((async) { - var events = []; - StreamSubscription subscription; - var periodic = new Stream.periodic(const Duration(minutes: 1), - (i) => i); - subscription = periodic.listen(events.add, cancelOnError: true); - async.elapse(const Duration(minutes: 3)); - subscription.cancel(); - expect(events, [0, 1, 2]); - }); - - }); - - test('should work with Stream.timeout', () { - new FakeAsync().run((async) { - var events = []; - var errors = []; - var controller = new StreamController(); - var timed = controller.stream.timeout(const Duration(minutes: 2)); - var subscription = timed.listen(events.add, onError: errors.add, - cancelOnError: true); - controller.add(0); - async.elapse(const Duration(minutes: 1)); - expect(events, [0]); - async.elapse(const Duration(minutes: 1)); - subscription.cancel(); - expect(errors, hasLength(1)); - expect(errors.first, new isInstanceOf()); - return controller.close(); - }); - }); - }); - }); - - group('flushMicrotasks', () { - test('should flush a microtask', () { - new FakeAsync().run((async) { - bool microtaskRan = false; - new Future.microtask(() { - microtaskRan = true; - }); - expect(microtaskRan, isFalse, - reason: 'should not flush until asked to'); - async.flushMicrotasks(); - expect(microtaskRan, isTrue); - }); - }); - test('should flush microtasks scheduled by microtasks in order', () { - new FakeAsync().run((async) { - final log = []; - new Future.microtask(() { - log.add(1); - new Future.microtask(() { - log.add(3); - }); - }); - new Future.microtask(() { - log.add(2); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushMicrotasks(); - expect(log, [1, 2, 3]); - }); - }); - test('should not run timers', () { - new FakeAsync().run((async) { - final log = []; - new Future.microtask(() { - log.add(1); - }); - new Future(() { - log.add(2); - }); - new Timer.periodic(new Duration(seconds: 1), (_) { - log.add(2); - }); - async.flushMicrotasks(); - expect(log, [1]); - }); - }); - }); - - group('flushTimers', () { - test('should flush timers', () { - new FakeAsync().run((async) { - final log = []; - new Future(() { - log.add(2); - new Future.delayed(elapseBy, () { - log.add(3); - }); - }); - new Future(() { - log.add(1); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushTimers(timeout: elapseBy * 2, flushPeriodicTimers: false); - expect(log, [1, 2, 3]); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); - }); - }); - - test('should run collateral periodic timers', () { - new FakeAsync().run((async) { - final log = []; - new Future.delayed(new Duration(seconds: 2), () { - log.add('delayed'); - }); - new Timer.periodic(new Duration(seconds: 1), (_) { - log.add('periodic'); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushTimers(flushPeriodicTimers: false); - expect(log, ['periodic', 'periodic', 'delayed']); - }); - }); - - test('should timeout', () { - new FakeAsync().run((async) { - int count = 0; - // Schedule 3 timers. All but the last one should fire. - for (int delay in [30, 60, 90]) { - new Future.delayed(new Duration(minutes: delay), () { - count++; - }); - } - expect(() => async.flushTimers(flushPeriodicTimers: false), - throwsStateError); - expect(count, 2); - }); - }); - - test('should timeout a chain of timers', () { - new FakeAsync().run((async) { - int count = 0; - createTimer() { - new Future.delayed(new Duration(minutes: 30), () { - count++; - createTimer(); - }); - } - createTimer(); - expect(() => async.flushTimers(timeout: new Duration(hours: 2), - flushPeriodicTimers: false), throwsStateError); - expect(count, 4); - }); - }); - - test('should timeout periodic timers', () { - new FakeAsync().run((async) { - int count = 0; - new Timer.periodic(new Duration(minutes: 30), (Timer timer) { - count++; - }); - expect(() => async.flushTimers(timeout: new Duration(hours: 1)), - throwsStateError); - expect(count, 2); - }); - }); - - test('should flush periodic timers', () { - new FakeAsync().run((async) { - int count = 0; - new Timer.periodic(new Duration(minutes: 30), (Timer timer) { - if (count == 3) { - timer.cancel(); - } - count++; - }); - async.flushTimers(timeout: new Duration(hours: 20)); - expect(count, 4); - }); - }); - - test('should compute absolute timeout as elapsed + timeout', () { - new FakeAsync().run((async) { - final log = []; - int count = 0; - createTimer() { - new Future.delayed(new Duration(minutes: 30), () { - log.add(count); - count++; - if (count < 4) { - createTimer(); - } - }); - } - createTimer(); - async.elapse(new Duration(hours: 1)); - async.flushTimers(timeout: new Duration(hours: 1)); - expect(count, 4); - }); - }); - }); - }); -} +// 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. + +library quiver.testing.async.fake_async_test; + +import 'dart:async'; + +import 'package:quiver/testing/async.dart'; +import 'package:unittest/unittest.dart'; + +main() { + group('FakeAsync', () { + + var initialTime = new DateTime(2000); + var elapseBy = const Duration(days: 1); + + test('should set initial time', () { + expect(new FakeAsync().getClock(initialTime).now(), initialTime); + }); + + group('elapseBlocking', () { + + test('should elapse time without calling timers', () { + var timerCalled = false; + new Timer(elapseBy ~/ 2, () => timerCalled = true); + new FakeAsync().elapseBlocking(elapseBy); + expect(timerCalled, isFalse); + }); + + test('should elapse time by the specified amount', () { + var it = new FakeAsync(); + it.elapseBlocking(elapseBy); + expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); + }); + + test('should throw when called with a negative duration', + () { + expect(() { + new FakeAsync().elapseBlocking(const Duration(days: -1)); + }, throwsA(new isInstanceOf())); + }); + + }); + + group('elapse', () { + + test('should elapse time by the specified amount', () { + new FakeAsync().run((async) { + async.elapse(elapseBy); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + }); + }); + + test('should throw ArgumentError when called with a negative duration', + () { + expect( + () => new FakeAsync().elapse(const Duration(days: -1)), + throwsA(new isInstanceOf())); + }); + + test('should throw when called before previous call is complete', () { + new FakeAsync().run((async) { + var error; + new Timer(elapseBy ~/ 2, () { + try { async.elapse(elapseBy); } + catch (e) { + error = e; + } + }); + async.elapse(elapseBy); + expect(error, new isInstanceOf()); + }); + }); + + group('when creating timers', () { + + test('should call timers expiring before or at end time', () { + new FakeAsync().run((async) { + var beforeCallCount = 0; + var atCallCount = 0; + new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); + new Timer(elapseBy, () {atCallCount++;}); + async.elapse(elapseBy); + expect(beforeCallCount, 1); + expect(atCallCount, 1); + }); + }); + + test('should call timers expiring due to elapseBlocking', () { + new FakeAsync().run((async) { + bool secondaryCalled = false; + new Timer(elapseBy, () { async.elapseBlocking(elapseBy); }); + new Timer(elapseBy * 2, () { secondaryCalled = true; }); + async.elapse(elapseBy); + expect(secondaryCalled, isTrue); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); + }); + }); + + test('should call timers at their scheduled time', () { + new FakeAsync().run((async) { + DateTime calledAt; + var periodicCalledAt = []; + new Timer(elapseBy ~/ 2, () {calledAt = async.getClock(initialTime).now();}); + new Timer.periodic(elapseBy ~/ 2, (_) { + periodicCalledAt.add(async.getClock(initialTime).now());}); + async.elapse(elapseBy); + expect(calledAt, initialTime.add(elapseBy ~/ 2)); + expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] + .map(initialTime.add)); + }); + }); + + test('should not call timers expiring after end time', () { + new FakeAsync().run((async) { + var timerCallCount = 0; + new Timer(elapseBy * 2, () {timerCallCount++;}); + async.elapse(elapseBy); + expect(timerCallCount, 0); + }); + }); + + test('should not call canceled timers', () { + new FakeAsync().run((async) { + int timerCallCount = 0; + var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); + timer.cancel(); + async.elapse(elapseBy); + expect(timerCallCount, 0); + }); + }); + + test('should call periodic timers each time the duration elapses', () { + new FakeAsync().run((async) { + var periodicCallCount = 0; + new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); + async.elapse(elapseBy); + expect(periodicCallCount, 10); + }); + }); + + test('should process microtasks surrounding each timer', () { + new FakeAsync().run((async) { + var microtaskCalls = 0; + var timerCalls = 0; + scheduleMicrotasks() { + for(int i = 0; i < 5; i++) { + scheduleMicrotask(() => microtaskCalls++); + } + } + scheduleMicrotasks(); + new Timer.periodic(elapseBy ~/ 5, (_) { + timerCalls++; + expect(microtaskCalls, 5 * timerCalls); + scheduleMicrotasks(); + }); + async.elapse(elapseBy); + expect(timerCalls, 5); + expect(microtaskCalls, 5 * (timerCalls + 1)); + }); + }); + + test('should pass the periodic timer itself to callbacks', () { + new FakeAsync().run((async) { + Timer passedTimer; + Timer periodic = new Timer.periodic(elapseBy, + (timer) {passedTimer = timer;}); + async.elapse(elapseBy); + expect(periodic, same(passedTimer)); + }); + }); + + test('should call microtasks before advancing time', () { + new FakeAsync().run((async) { + DateTime calledAt; + scheduleMicrotask((){ + calledAt = async.getClock(initialTime).now(); + }); + async.elapse(const Duration(minutes: 1)); + expect(calledAt, initialTime); + }); + }); + + test('should add event before advancing time', () { + return new Future(() => new FakeAsync().run((async) { + var controller = new StreamController(); + var ret = controller.stream.first.then((_) { + expect(async.getClock(initialTime).now(), initialTime); + }); + controller.add(null); + async.elapse(const Duration(minutes: 1)); + return ret; + })); + }); + + test('should increase negative duration timers to zero duration', () { + new FakeAsync().run((async) { + var negativeDuration = const Duration(days: -1); + DateTime calledAt; + new Timer(negativeDuration, () { + calledAt = async.getClock(initialTime).now(); + }); + async.elapse(const Duration(minutes: 1)); + expect(calledAt, initialTime); + }); + }); + + test('should not be additive with elapseBlocking', () { + new FakeAsync().run((async) { + new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); + async.elapse(elapseBy); + expect(async.getClock(initialTime).now(), + initialTime.add(elapseBy * 5)); + }); + }); + + group('isActive', () { + + test('should be false after timer is run', () { + new FakeAsync().run((async) { + var timer = new Timer(elapseBy ~/ 2, () {}); + async.elapse(elapseBy); + expect(timer.isActive, isFalse); + }); + }); + + test('should be true after periodic timer is run', () { + new FakeAsync().run((async) { + var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); + async.elapse(elapseBy); + expect(timer.isActive, isTrue); + }); + }); + + test('should be false after timer is canceled', () { + new FakeAsync().run((async) { + var timer = new Timer(elapseBy ~/ 2, () {}); + timer.cancel(); + expect(timer.isActive, isFalse); + }); + }); + + }); + + test('should work with new Future()', () { + new FakeAsync().run((async) { + var callCount = 0; + new Future(() => callCount++); + async.elapse(Duration.ZERO); + expect(callCount, 1); + }); + }); + + test('should work with Future.delayed', () { + new FakeAsync().run((async) { + int result; + new Future.delayed(elapseBy, () => result = 5); + async.elapse(elapseBy); + expect(result, 5); + }); + }); + + test('should work with Future.timeout', () { + new FakeAsync().run((async) { + var completer = new Completer(); + var timed = completer.future.timeout(elapseBy ~/ 2); + expect(timed, throwsA(new isInstanceOf())); + async.elapse(elapseBy); + completer.complete(); + }); + }); + + // TODO: Pausing and resuming the timeout Stream doesn't work since + // it uses `new Stopwatch()`. + // + // See https://code.google.com/p/dart/issues/detail?id=18149 + test('should work with Stream.periodic', () { + new FakeAsync().run((async) { + var events = []; + StreamSubscription subscription; + var periodic = new Stream.periodic(const Duration(minutes: 1), + (i) => i); + subscription = periodic.listen(events.add, cancelOnError: true); + async.elapse(const Duration(minutes: 3)); + subscription.cancel(); + expect(events, [0, 1, 2]); + }); + + }); + + test('should work with Stream.timeout', () { + new FakeAsync().run((async) { + var events = []; + var errors = []; + var controller = new StreamController(); + var timed = controller.stream.timeout(const Duration(minutes: 2)); + var subscription = timed.listen(events.add, onError: errors.add, + cancelOnError: true); + controller.add(0); + async.elapse(const Duration(minutes: 1)); + expect(events, [0]); + async.elapse(const Duration(minutes: 1)); + subscription.cancel(); + expect(errors, hasLength(1)); + expect(errors.first, new isInstanceOf()); + return controller.close(); + }); + }); + }); + }); + + group('flushMicrotasks', () { + test('should flush a microtask', () { + new FakeAsync().run((async) { + bool microtaskRan = false; + new Future.microtask(() { + microtaskRan = true; + }); + expect(microtaskRan, isFalse, + reason: 'should not flush until asked to'); + async.flushMicrotasks(); + expect(microtaskRan, isTrue); + }); + }); + test('should flush microtasks scheduled by microtasks in order', () { + new FakeAsync().run((async) { + final log = []; + new Future.microtask(() { + log.add(1); + new Future.microtask(() { + log.add(3); + }); + }); + new Future.microtask(() { + log.add(2); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushMicrotasks(); + expect(log, [1, 2, 3]); + }); + }); + test('should not run timers', () { + new FakeAsync().run((async) { + final log = []; + new Future.microtask(() { + log.add(1); + }); + new Future(() { + log.add(2); + }); + new Timer.periodic(new Duration(seconds: 1), (_) { + log.add(2); + }); + async.flushMicrotasks(); + expect(log, [1]); + }); + }); + }); + + group('flushTimers', () { + test('should flush timers', () { + new FakeAsync().run((async) { + final log = []; + new Future(() { + log.add(2); + new Future.delayed(elapseBy, () { + log.add(3); + }); + }); + new Future(() { + log.add(1); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushTimers(timeout: elapseBy * 2, flushPeriodicTimers: false); + expect(log, [1, 2, 3]); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + }); + }); + + test('should run collateral periodic timers', () { + new FakeAsync().run((async) { + final log = []; + new Future.delayed(new Duration(seconds: 2), () { + log.add('delayed'); + }); + new Timer.periodic(new Duration(seconds: 1), (_) { + log.add('periodic'); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushTimers(flushPeriodicTimers: false); + expect(log, ['periodic', 'periodic', 'delayed']); + }); + }); + + test('should timeout', () { + new FakeAsync().run((async) { + int count = 0; + // Schedule 3 timers. All but the last one should fire. + for (int delay in [30, 60, 90]) { + new Future.delayed(new Duration(minutes: delay), () { + count++; + }); + } + expect(() => async.flushTimers(flushPeriodicTimers: false), + throwsStateError); + expect(count, 2); + }); + }); + + test('should timeout a chain of timers', () { + new FakeAsync().run((async) { + int count = 0; + createTimer() { + new Future.delayed(new Duration(minutes: 30), () { + count++; + createTimer(); + }); + } + createTimer(); + expect(() => async.flushTimers(timeout: new Duration(hours: 2), + flushPeriodicTimers: false), throwsStateError); + expect(count, 4); + }); + }); + + test('should timeout periodic timers', () { + new FakeAsync().run((async) { + int count = 0; + new Timer.periodic(new Duration(minutes: 30), (Timer timer) { + count++; + }); + expect(() => async.flushTimers(timeout: new Duration(hours: 1)), + throwsStateError); + expect(count, 2); + }); + }); + + test('should flush periodic timers', () { + new FakeAsync().run((async) { + int count = 0; + new Timer.periodic(new Duration(minutes: 30), (Timer timer) { + if (count == 3) { + timer.cancel(); + } + count++; + }); + async.flushTimers(timeout: new Duration(hours: 20)); + expect(count, 4); + }); + }); + + test('should compute absolute timeout as elapsed + timeout', () { + new FakeAsync().run((async) { + final log = []; + int count = 0; + createTimer() { + new Future.delayed(new Duration(minutes: 30), () { + log.add(count); + count++; + if (count < 4) { + createTimer(); + } + }); + } + createTimer(); + async.elapse(new Duration(hours: 1)); + async.flushTimers(timeout: new Duration(hours: 1)); + expect(count, 4); + }); + }); + }); + }); +} From 61566b2ddefd553922870c22ef423ae245c44160 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 3 Feb 2015 16:18:35 -0800 Subject: [PATCH 026/113] Fix hanging FakeAsync test. --- pkgs/fake_async/test/fake_async_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index e44b9fe7a..0a5e36acf 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -33,9 +33,10 @@ main() { test('should elapse time without calling timers', () { var timerCalled = false; - new Timer(elapseBy ~/ 2, () => timerCalled = true); + var timer = new Timer(elapseBy ~/ 2, () => timerCalled = true); new FakeAsync().elapseBlocking(elapseBy); expect(timerCalled, isFalse); + timer.cancel(); }); test('should elapse time by the specified amount', () { From 70692d38d286c26f313dc9e1ecc988b3a851775a Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 4 Feb 2015 23:17:19 -0800 Subject: [PATCH 027/113] Reformat with dartformat --- pkgs/fake_async/lib/fake_async.dart | 34 ++----- pkgs/fake_async/test/fake_async_test.dart | 103 ++++++++++++---------- 2 files changed, 64 insertions(+), 73 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 736072a0d..486f13077 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -37,7 +37,6 @@ part of quiver.testing.async; /// }); /// }); abstract class FakeAsync { - factory FakeAsync() = _FakeAsync; FakeAsync._(); @@ -110,7 +109,6 @@ abstract class FakeAsync { } class _FakeAsync extends FakeAsync { - Duration _elapsed = Duration.ZERO; Duration _elapsingTo; Queue _microtasks = new Queue(); @@ -178,29 +176,13 @@ class _FakeAsync extends FakeAsync { Zone _zone; 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); - }); + 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(); @@ -249,11 +231,9 @@ class _FakeAsync extends FakeAsync { _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; diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 0a5e36acf..d8fe36ee7 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -21,7 +21,6 @@ import 'package:unittest/unittest.dart'; main() { group('FakeAsync', () { - var initialTime = new DateTime(2000); var elapseBy = const Duration(days: 1); @@ -30,7 +29,6 @@ main() { }); group('elapseBlocking', () { - test('should elapse time without calling timers', () { var timerCalled = false; var timer = new Timer(elapseBy ~/ 2, () => timerCalled = true); @@ -45,17 +43,14 @@ main() { expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); }); - test('should throw when called with a negative duration', - () { - expect(() { - new FakeAsync().elapseBlocking(const Duration(days: -1)); - }, throwsA(new isInstanceOf())); - }); - + test('should throw when called with a negative duration', () { + expect(() { + new FakeAsync().elapseBlocking(const Duration(days: -1)); + }, throwsA(new isInstanceOf())); + }); }); group('elapse', () { - test('should elapse time by the specified amount', () { new FakeAsync().run((async) { async.elapse(elapseBy); @@ -65,17 +60,17 @@ main() { test('should throw ArgumentError when called with a negative duration', () { - expect( - () => new FakeAsync().elapse(const Duration(days: -1)), - throwsA(new isInstanceOf())); - }); + expect(() => new FakeAsync().elapse(const Duration(days: -1)), + throwsA(new isInstanceOf())); + }); test('should throw when called before previous call is complete', () { new FakeAsync().run((async) { var error; new Timer(elapseBy ~/ 2, () { - try { async.elapse(elapseBy); } - catch (e) { + try { + async.elapse(elapseBy); + } catch (e) { error = e; } }); @@ -85,13 +80,16 @@ main() { }); group('when creating timers', () { - test('should call timers expiring before or at end time', () { new FakeAsync().run((async) { var beforeCallCount = 0; var atCallCount = 0; - new Timer(elapseBy ~/ 2, () {beforeCallCount++;}); - new Timer(elapseBy, () {atCallCount++;}); + new Timer(elapseBy ~/ 2, () { + beforeCallCount++; + }); + new Timer(elapseBy, () { + atCallCount++; + }); async.elapse(elapseBy); expect(beforeCallCount, 1); expect(atCallCount, 1); @@ -101,32 +99,42 @@ main() { test('should call timers expiring due to elapseBlocking', () { new FakeAsync().run((async) { bool secondaryCalled = false; - new Timer(elapseBy, () { async.elapseBlocking(elapseBy); }); - new Timer(elapseBy * 2, () { secondaryCalled = true; }); + new Timer(elapseBy, () { + async.elapseBlocking(elapseBy); + }); + new Timer(elapseBy * 2, () { + secondaryCalled = true; + }); async.elapse(elapseBy); expect(secondaryCalled, isTrue); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); + expect(async.getClock(initialTime).now(), + initialTime.add(elapseBy * 2)); }); }); test('should call timers at their scheduled time', () { new FakeAsync().run((async) { DateTime calledAt; - var periodicCalledAt = []; - new Timer(elapseBy ~/ 2, () {calledAt = async.getClock(initialTime).now();}); + var periodicCalledAt = []; + new Timer(elapseBy ~/ 2, () { + calledAt = async.getClock(initialTime).now(); + }); new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(async.getClock(initialTime).now());}); + periodicCalledAt.add(async.getClock(initialTime).now()); + }); async.elapse(elapseBy); expect(calledAt, initialTime.add(elapseBy ~/ 2)); - expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy] - .map(initialTime.add)); + expect(periodicCalledAt, + [elapseBy ~/ 2, elapseBy].map(initialTime.add)); }); }); test('should not call timers expiring after end time', () { new FakeAsync().run((async) { var timerCallCount = 0; - new Timer(elapseBy * 2, () {timerCallCount++;}); + new Timer(elapseBy * 2, () { + timerCallCount++; + }); async.elapse(elapseBy); expect(timerCallCount, 0); }); @@ -135,7 +143,9 @@ main() { test('should not call canceled timers', () { new FakeAsync().run((async) { int timerCallCount = 0; - var timer = new Timer(elapseBy ~/ 2, () {timerCallCount++;}); + var timer = new Timer(elapseBy ~/ 2, () { + timerCallCount++; + }); timer.cancel(); async.elapse(elapseBy); expect(timerCallCount, 0); @@ -145,7 +155,9 @@ main() { test('should call periodic timers each time the duration elapses', () { new FakeAsync().run((async) { var periodicCallCount = 0; - new Timer.periodic(elapseBy ~/ 10, (_) {periodicCallCount++;}); + new Timer.periodic(elapseBy ~/ 10, (_) { + periodicCallCount++; + }); async.elapse(elapseBy); expect(periodicCallCount, 10); }); @@ -156,7 +168,7 @@ main() { var microtaskCalls = 0; var timerCalls = 0; scheduleMicrotasks() { - for(int i = 0; i < 5; i++) { + for (int i = 0; i < 5; i++) { scheduleMicrotask(() => microtaskCalls++); } } @@ -175,8 +187,9 @@ main() { test('should pass the periodic timer itself to callbacks', () { new FakeAsync().run((async) { Timer passedTimer; - Timer periodic = new Timer.periodic(elapseBy, - (timer) {passedTimer = timer;}); + Timer periodic = new Timer.periodic(elapseBy, (timer) { + passedTimer = timer; + }); async.elapse(elapseBy); expect(periodic, same(passedTimer)); }); @@ -185,7 +198,7 @@ main() { test('should call microtasks before advancing time', () { new FakeAsync().run((async) { DateTime calledAt; - scheduleMicrotask((){ + scheduleMicrotask(() { calledAt = async.getClock(initialTime).now(); }); async.elapse(const Duration(minutes: 1)); @@ -227,7 +240,6 @@ main() { }); group('isActive', () { - test('should be false after timer is run', () { new FakeAsync().run((async) { var timer = new Timer(elapseBy ~/ 2, () {}); @@ -238,7 +250,7 @@ main() { test('should be true after periodic timer is run', () { new FakeAsync().run((async) { - var timer= new Timer.periodic(elapseBy ~/ 2, (_) {}); + var timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); async.elapse(elapseBy); expect(timer.isActive, isTrue); }); @@ -251,7 +263,6 @@ main() { expect(timer.isActive, isFalse); }); }); - }); test('should work with new Future()', () { @@ -288,26 +299,25 @@ main() { // See https://code.google.com/p/dart/issues/detail?id=18149 test('should work with Stream.periodic', () { new FakeAsync().run((async) { - var events = []; + var events = []; StreamSubscription subscription; - var periodic = new Stream.periodic(const Duration(minutes: 1), - (i) => i); + var periodic = + new Stream.periodic(const Duration(minutes: 1), (i) => i); subscription = periodic.listen(events.add, cancelOnError: true); async.elapse(const Duration(minutes: 3)); subscription.cancel(); expect(events, [0, 1, 2]); }); - }); test('should work with Stream.timeout', () { new FakeAsync().run((async) { - var events = []; + var events = []; var errors = []; var controller = new StreamController(); var timed = controller.stream.timeout(const Duration(minutes: 2)); - var subscription = timed.listen(events.add, onError: errors.add, - cancelOnError: true); + var subscription = timed.listen(events.add, + onError: errors.add, cancelOnError: true); controller.add(0); async.elapse(const Duration(minutes: 1)); expect(events, [0]); @@ -429,8 +439,9 @@ main() { }); } createTimer(); - expect(() => async.flushTimers(timeout: new Duration(hours: 2), - flushPeriodicTimers: false), throwsStateError); + expect(() => async.flushTimers( + timeout: new Duration(hours: 2), flushPeriodicTimers: false), + throwsStateError); expect(count, 4); }); }); From 96bb3539def396a15a97791e5a234b0f1d74e79c Mon Sep 17 00:00:00 2001 From: zoechi Date: Fri, 8 May 2015 19:13:04 +0200 Subject: [PATCH 028/113] Update to new test package and new matcher. --- pkgs/fake_async/test/fake_async_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index d8fe36ee7..4057181ff 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -17,7 +17,7 @@ library quiver.testing.async.fake_async_test; import 'dart:async'; import 'package:quiver/testing/async.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; main() { group('FakeAsync', () { From 3e1faca6d3c2f5210b1f2359f5e9e345706ff269 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 14 May 2015 08:51:03 +0200 Subject: [PATCH 029/113] Add stats reporting to FakeAsync fixes dart-lang/fake_async#248 --- pkgs/fake_async/lib/fake_async.dart | 24 +++++++++++++ pkgs/fake_async/test/fake_async_test.dart | 43 +++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 486f13077..eb1476971 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -106,6 +106,15 @@ abstract class FakeAsync { /// 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 { @@ -118,9 +127,11 @@ class _FakeAsync extends FakeAsync { _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'); @@ -134,6 +145,7 @@ class _FakeAsync extends FakeAsync { _elapsingTo = null; } + @override void elapseBlocking(Duration duration) { if (duration.inMicroseconds < 0) { throw new ArgumentError('Cannot call elapse with negative duration'); @@ -167,6 +179,7 @@ class _FakeAsync extends FakeAsync { }); } + @override run(callback(FakeAsync self)) { if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); @@ -175,6 +188,17 @@ class _FakeAsync extends FakeAsync { } 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); diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 4057181ff..4f20fb099 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -492,5 +492,48 @@ main() { }); }); }); + + group('stats', () { + test('should report the number of pending microtasks', () { + new FakeAsync().run((async) { + expect(async.microtaskCount, 0); + scheduleMicrotask(() => null); + expect(async.microtaskCount, 1); + scheduleMicrotask(() => null); + expect(async.microtaskCount, 2); + async.flushMicrotasks(); + expect(async.microtaskCount, 0); + }); + }); + + test('it should report the number of pending periodic timers', () { + new FakeAsync().run((async) { + expect(async.periodicTimerCount, 0); + Timer timer = new Timer.periodic(new Duration(minutes: 30), + (Timer timer) { }); + expect(async.periodicTimerCount, 1); + new Timer.periodic(new Duration(minutes: 20), (Timer timer) { }); + expect(async.periodicTimerCount, 2); + async.elapse(new Duration(minutes: 20)); + expect(async.periodicTimerCount, 2); + timer.cancel(); + expect(async.periodicTimerCount, 1); + }); + }); + + test('it should report the number of pending non periodic timers', () { + new FakeAsync().run((async) { + expect(async.nonPeriodicTimerCount, 0); + Timer timer = new Timer(new Duration(minutes: 30), () { }); + expect(async.nonPeriodicTimerCount, 1); + new Timer(new Duration(minutes: 20), () { }); + expect(async.nonPeriodicTimerCount, 2); + async.elapse(new Duration(minutes: 25)); + expect(async.nonPeriodicTimerCount, 1); + timer.cancel(); + expect(async.nonPeriodicTimerCount, 0); + }); + }); + }); }); } From e481654788eb6299462da80926c02bfc4b604481 Mon Sep 17 00:00:00 2001 From: Yegor Jbanov Date: Wed, 24 Jun 2015 10:12:01 -0700 Subject: [PATCH 030/113] fix Future.timeout test in fake_async_test.dart --- pkgs/fake_async/test/fake_async_test.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 4f20fb099..bc7828510 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -286,9 +286,12 @@ main() { test('should work with Future.timeout', () { new FakeAsync().run((async) { var completer = new Completer(); - var timed = completer.future.timeout(elapseBy ~/ 2); - expect(timed, throwsA(new isInstanceOf())); + TimeoutException timeout; + completer.future.timeout(elapseBy ~/ 2).catchError((err) { + timeout = err; + }); async.elapse(elapseBy); + expect(timeout, new isInstanceOf()); completer.complete(); }); }); From 195ddacb604bf6c5033171eddf359ec82bf5b593 Mon Sep 17 00:00:00 2001 From: Yegor Jbanov Date: Wed, 24 Jun 2015 10:12:37 -0700 Subject: [PATCH 031/113] _FakeAsync implements FakeAsync No more need for dummy constructors --- pkgs/fake_async/lib/fake_async.dart | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index eb1476971..8537683b5 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -39,8 +39,6 @@ part of quiver.testing.async; abstract class FakeAsync { factory FakeAsync() = _FakeAsync; - FakeAsync._(); - /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and /// [elapseBlocking]. /// @@ -89,8 +87,9 @@ abstract class FakeAsync { /// 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)); + /// Calls [callback] with `this` as argument and returns the result returned + /// by [callback]. + dynamic run(callback(FakeAsync self)); /// Runs all remaining microtasks, including those scheduled as a result of /// running them, until there are no more microtasks scheduled. @@ -117,16 +116,12 @@ abstract class FakeAsync { int get microtaskCount; } -class _FakeAsync extends FakeAsync { +class _FakeAsync implements FakeAsync { Duration _elapsed = Duration.ZERO; Duration _elapsingTo; Queue _microtasks = new Queue(); Set<_FakeTimer> _timers = new Set<_FakeTimer>(); - _FakeAsync() : super._() { - _elapsed; - } - @override Clock getClock(DateTime initialTime) => new Clock(() => initialTime.add(_elapsed)); From 86887d96afc26e6115efde3f026535475bb1c843 Mon Sep 17 00:00:00 2001 From: John Thomas McDole Date: Sun, 10 Jan 2016 17:53:02 -0800 Subject: [PATCH 032/113] Non-periodic Timers are not active while executing callback() Fixes dart-lang/fake_async#277 Both the DartVM and dartpad.dartlang.org mark a timer as not-active while the callback is executing. This can be used in production code to test some condition - but fails when using quiver-dart/async.dart. --- pkgs/fake_async/lib/fake_async.dart | 2 +- pkgs/fake_async/test/fake_async_test.dart | 50 ++++++++++++++++------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 8537683b5..7e9bc5bc9 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -236,8 +236,8 @@ class _FakeAsync implements FakeAsync { timer._callback(timer); timer._nextCall += timer._duration; } else { - timer._callback(); _timers.remove(timer); + timer._callback(); } } diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index bc7828510..b855efd15 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -208,14 +208,14 @@ main() { test('should add event before advancing time', () { return new Future(() => new FakeAsync().run((async) { - var controller = new StreamController(); - var ret = controller.stream.first.then((_) { - expect(async.getClock(initialTime).now(), initialTime); - }); - controller.add(null); - async.elapse(const Duration(minutes: 1)); - return ret; - })); + var controller = new StreamController(); + var ret = controller.stream.first.then((_) { + expect(async.getClock(initialTime).now(), initialTime); + }); + controller.add(null); + async.elapse(const Duration(minutes: 1)); + return ret; + })); }); test('should increase negative duration timers to zero duration', () { @@ -442,7 +442,8 @@ main() { }); } createTimer(); - expect(() => async.flushTimers( + expect( + () => async.flushTimers( timeout: new Duration(hours: 2), flushPeriodicTimers: false), throwsStateError); expect(count, 4); @@ -512,10 +513,10 @@ main() { test('it should report the number of pending periodic timers', () { new FakeAsync().run((async) { expect(async.periodicTimerCount, 0); - Timer timer = new Timer.periodic(new Duration(minutes: 30), - (Timer timer) { }); + Timer timer = + new Timer.periodic(new Duration(minutes: 30), (Timer timer) {}); expect(async.periodicTimerCount, 1); - new Timer.periodic(new Duration(minutes: 20), (Timer timer) { }); + new Timer.periodic(new Duration(minutes: 20), (Timer timer) {}); expect(async.periodicTimerCount, 2); async.elapse(new Duration(minutes: 20)); expect(async.periodicTimerCount, 2); @@ -527,9 +528,9 @@ main() { test('it should report the number of pending non periodic timers', () { new FakeAsync().run((async) { expect(async.nonPeriodicTimerCount, 0); - Timer timer = new Timer(new Duration(minutes: 30), () { }); + Timer timer = new Timer(new Duration(minutes: 30), () {}); expect(async.nonPeriodicTimerCount, 1); - new Timer(new Duration(minutes: 20), () { }); + new Timer(new Duration(minutes: 20), () {}); expect(async.nonPeriodicTimerCount, 2); async.elapse(new Duration(minutes: 25)); expect(async.nonPeriodicTimerCount, 1); @@ -538,5 +539,26 @@ main() { }); }); }); + + group('timers', () { + test('should behave like real timers', () { + return new FakeAsync().run((async) { + var timeout = const Duration(minutes: 1); + int counter = 0; + var timer; + timer = new Timer(timeout, () { + counter++; + expect(timer.isActive, isFalse, + reason: "is not active while executing callback"); + }); + expect(timer.isActive, isTrue, + reason: "is active before executing callback"); + async.elapse(timeout); + expect(counter, equals(1), reason: "timer executed"); + expect(timer.isActive, isFalse, + reason: "is not active after executing callback"); + }); + }); + }); }); } From 977c00e2225dd5bc8617d2ed5fed51337fee75d7 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 21 Sep 2016 16:02:35 -0700 Subject: [PATCH 033/113] Fix FakeAsync timeout test Dart SDK commit 6255638cd0623b3aa41596b98f4584876f6b8822 introduced changes to stream subscription cancellation. cancelOnError was irrelevant to the periodic and timeout tests. --- pkgs/fake_async/test/fake_async_test.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index b855efd15..39c7a7627 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -306,10 +306,10 @@ main() { StreamSubscription subscription; var periodic = new Stream.periodic(const Duration(minutes: 1), (i) => i); - subscription = periodic.listen(events.add, cancelOnError: true); + subscription = periodic.listen(events.add); async.elapse(const Duration(minutes: 3)); - subscription.cancel(); expect(events, [0, 1, 2]); + subscription.cancel(); }); }); @@ -319,16 +319,15 @@ main() { var errors = []; var controller = new StreamController(); var timed = controller.stream.timeout(const Duration(minutes: 2)); - var subscription = timed.listen(events.add, - onError: errors.add, cancelOnError: true); + var subscription = timed.listen(events.add, onError: errors.add); controller.add(0); async.elapse(const Duration(minutes: 1)); expect(events, [0]); async.elapse(const Duration(minutes: 1)); - subscription.cancel(); expect(errors, hasLength(1)); expect(errors.first, new isInstanceOf()); - return controller.close(); + subscription.cancel(); + controller.close(); }); }); }); From a69a33a90336a9372c2fc1808a8da299734abd39 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Thu, 3 Nov 2016 10:50:56 -0700 Subject: [PATCH 034/113] Use Duration const constructor where possible (dart-lang/fake_async#316) --- pkgs/fake_async/test/fake_async_test.dart | 40 +++++++++++++---------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 39c7a7627..17209e620 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -172,6 +172,7 @@ main() { scheduleMicrotask(() => microtaskCalls++); } } + scheduleMicrotasks(); new Timer.periodic(elapseBy ~/ 5, (_) { timerCalls++; @@ -372,7 +373,7 @@ main() { new Future(() { log.add(2); }); - new Timer.periodic(new Duration(seconds: 1), (_) { + new Timer.periodic(const Duration(seconds: 1), (_) { log.add(2); }); async.flushMicrotasks(); @@ -404,10 +405,10 @@ main() { test('should run collateral periodic timers', () { new FakeAsync().run((async) { final log = []; - new Future.delayed(new Duration(seconds: 2), () { + new Future.delayed(const Duration(seconds: 2), () { log.add('delayed'); }); - new Timer.periodic(new Duration(seconds: 1), (_) { + new Timer.periodic(const Duration(seconds: 1), (_) { log.add('periodic'); }); expect(log, hasLength(0), reason: 'should not flush until asked to'); @@ -435,15 +436,17 @@ main() { new FakeAsync().run((async) { int count = 0; createTimer() { - new Future.delayed(new Duration(minutes: 30), () { + new Future.delayed(const Duration(minutes: 30), () { count++; createTimer(); }); } + createTimer(); expect( () => async.flushTimers( - timeout: new Duration(hours: 2), flushPeriodicTimers: false), + timeout: const Duration(hours: 2), + flushPeriodicTimers: false), throwsStateError); expect(count, 4); }); @@ -452,10 +455,10 @@ main() { test('should timeout periodic timers', () { new FakeAsync().run((async) { int count = 0; - new Timer.periodic(new Duration(minutes: 30), (Timer timer) { + new Timer.periodic(const Duration(minutes: 30), (Timer timer) { count++; }); - expect(() => async.flushTimers(timeout: new Duration(hours: 1)), + expect(() => async.flushTimers(timeout: const Duration(hours: 1)), throwsStateError); expect(count, 2); }); @@ -464,13 +467,13 @@ main() { test('should flush periodic timers', () { new FakeAsync().run((async) { int count = 0; - new Timer.periodic(new Duration(minutes: 30), (Timer timer) { + new Timer.periodic(const Duration(minutes: 30), (Timer timer) { if (count == 3) { timer.cancel(); } count++; }); - async.flushTimers(timeout: new Duration(hours: 20)); + async.flushTimers(timeout: const Duration(hours: 20)); expect(count, 4); }); }); @@ -480,7 +483,7 @@ main() { final log = []; int count = 0; createTimer() { - new Future.delayed(new Duration(minutes: 30), () { + new Future.delayed(const Duration(minutes: 30), () { log.add(count); count++; if (count < 4) { @@ -488,9 +491,10 @@ main() { } }); } + createTimer(); - async.elapse(new Duration(hours: 1)); - async.flushTimers(timeout: new Duration(hours: 1)); + async.elapse(const Duration(hours: 1)); + async.flushTimers(timeout: const Duration(hours: 1)); expect(count, 4); }); }); @@ -513,11 +517,11 @@ main() { new FakeAsync().run((async) { expect(async.periodicTimerCount, 0); Timer timer = - new Timer.periodic(new Duration(minutes: 30), (Timer timer) {}); + new Timer.periodic(const Duration(minutes: 30), (Timer timer) {}); expect(async.periodicTimerCount, 1); - new Timer.periodic(new Duration(minutes: 20), (Timer timer) {}); + new Timer.periodic(const Duration(minutes: 20), (Timer timer) {}); expect(async.periodicTimerCount, 2); - async.elapse(new Duration(minutes: 20)); + async.elapse(const Duration(minutes: 20)); expect(async.periodicTimerCount, 2); timer.cancel(); expect(async.periodicTimerCount, 1); @@ -527,11 +531,11 @@ main() { test('it should report the number of pending non periodic timers', () { new FakeAsync().run((async) { expect(async.nonPeriodicTimerCount, 0); - Timer timer = new Timer(new Duration(minutes: 30), () {}); + Timer timer = new Timer(const Duration(minutes: 30), () {}); expect(async.nonPeriodicTimerCount, 1); - new Timer(new Duration(minutes: 20), () {}); + new Timer(const Duration(minutes: 20), () {}); expect(async.nonPeriodicTimerCount, 2); - async.elapse(new Duration(minutes: 25)); + async.elapse(const Duration(minutes: 25)); expect(async.nonPeriodicTimerCount, 1); timer.cancel(); expect(async.nonPeriodicTimerCount, 0); From 37afa57a7b5190934540ac67e7e86fa9539454d5 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 16 Nov 2016 09:40:49 -0800 Subject: [PATCH 035/113] Apply dartfmt 0.2.11+1 (dart-lang/fake_async#327) --- pkgs/fake_async/lib/fake_async.dart | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 7e9bc5bc9..376847c32 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -103,7 +103,8 @@ abstract class FakeAsync { /// [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), + void flushTimers( + {Duration timeout: const Duration(hours: 1), bool flushPeriodicTimers: true}); /// The number of created periodic timers that have not been canceled. @@ -157,7 +158,8 @@ class _FakeAsync implements FakeAsync { } @override - void flushTimers({Duration timeout: const Duration(hours: 1), + void flushTimers( + {Duration timeout: const Duration(hours: 1), bool flushPeriodicTimers: true}) { final absoluteTimeout = _elapsed + timeout; _drainTimersWhile((_FakeTimer timer) { @@ -181,6 +183,7 @@ class _FakeAsync implements FakeAsync { } return _zone.runGuarded(() => callback(this)); } + Zone _zone; @override @@ -195,13 +198,14 @@ class _FakeAsync implements FakeAsync { 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); - }); + 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(); From b006ce4e06ab79174b138f3813541b624e7a2b02 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Tue, 28 Mar 2017 09:14:37 -0700 Subject: [PATCH 036/113] fix strong mode errors, turn on strong mode analysis (dart-lang/fake_async#343) * fix strong mode errors, turn on strong mode analysis * Push better fix for _id inference --- pkgs/fake_async/.analysis_options | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pkgs/fake_async/.analysis_options diff --git a/pkgs/fake_async/.analysis_options b/pkgs/fake_async/.analysis_options new file mode 100644 index 000000000..a10d4c5a0 --- /dev/null +++ b/pkgs/fake_async/.analysis_options @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true From 439c51002ec1817aa6deaf6889ed810d3ce9484f Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Wed, 30 Aug 2017 21:09:45 +0200 Subject: [PATCH 037/113] Don't use the result of `runGuarded`. (dart-lang/fake_async#360) When the function to `runGuarded` throws synchronously, then the uncaught-handler is invoked. Except for very rare cases, that handler can only return `null`. As such, it doesn't make sense to use the result of `runGuarded`. Future versions of the Zone API will thus make `runGuarded` a `void` function. --- pkgs/fake_async/lib/fake_async.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 376847c32..1352caadf 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -181,7 +181,9 @@ class _FakeAsync implements FakeAsync { if (_zone == null) { _zone = Zone.current.fork(specification: _zoneSpec); } - return _zone.runGuarded(() => callback(this)); + var result; + _zone.runGuarded(() { result = callback(this); }); + return result; } Zone _zone; From 498f7e3185fb01bc8f54ea8f146fde2667956a4e Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Wed, 27 Sep 2017 15:00:14 -0700 Subject: [PATCH 038/113] dartfmt: no logical changes (dart-lang/fake_async#364) --- pkgs/fake_async/lib/fake_async.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 1352caadf..8b8dedf72 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -182,7 +182,9 @@ class _FakeAsync implements FakeAsync { _zone = Zone.current.fork(specification: _zoneSpec); } var result; - _zone.runGuarded(() { result = callback(this); }); + _zone.runGuarded(() { + result = callback(this); + }); return result; } From 54bbc394981f5b728c9a7eacc095992a8ca83ea9 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Thu, 12 Oct 2017 00:08:54 +0200 Subject: [PATCH 039/113] Various code cleanups; rename analysis_options * rename analysis_options * replace deprecated throws matcher * remove unnecessary cast --- pkgs/fake_async/.analysis_options | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 pkgs/fake_async/.analysis_options diff --git a/pkgs/fake_async/.analysis_options b/pkgs/fake_async/.analysis_options deleted file mode 100644 index a10d4c5a0..000000000 --- a/pkgs/fake_async/.analysis_options +++ /dev/null @@ -1,2 +0,0 @@ -analyzer: - strong-mode: true From bf03044491393181399f1ddf03a9f81e506a1fe8 Mon Sep 17 00:00:00 2001 From: Alexandre Ardhuin Date: Tue, 31 Oct 2017 23:06:43 +0100 Subject: [PATCH 040/113] Fix missing variable name in fake_async (dart-lang/fake_async#380) Fixes dart-lang/fake_async#379 --- pkgs/fake_async/lib/fake_async.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 8b8dedf72..ca3cb8f60 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -211,7 +211,7 @@ class _FakeAsync implements FakeAsync { _microtasks.add(microtask); }); - _drainTimersWhile(bool predicate(_FakeTimer)) { + _drainTimersWhile(bool predicate(_FakeTimer timer)) { _drainMicrotasks(); _FakeTimer next; while ((next = _getNextTimer()) != null && predicate(next)) { From 0a6b735231754abefff076bc21251861c81ebed9 Mon Sep 17 00:00:00 2001 From: Alec Henninger Date: Wed, 12 Aug 2015 23:47:51 -0400 Subject: [PATCH 041/113] Use FIFO timer ordering in FakeAsync (dart-lang/fake_async#265) As noted in https://github.com/google/quiver-dart/issues/258, the current ordering of _getNextTimer() in _FakeAsync is inconsistent with the native Dart event loop. In the process of fixing that, I had to fix a few other idiosyncrasies that were revealed from the existing unit tests which relied on the incorrect behavior. For instance, if you had a periodic timer occur at the same time as a delayed timer, whether or not both of them were "flushed" in flushTimers depended on the order with which they were queued. Now it does not. --- pkgs/fake_async/lib/fake_async.dart | 11 ++-- pkgs/fake_async/test/fake_async_test.dart | 67 +++++++++++++++++++++-- 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index ca3cb8f60..afa6ddae5 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -170,8 +170,10 @@ class _FakeAsync implements FakeAsync { if (flushPeriodicTimers) { return _timers.isNotEmpty; } else { - // translation: keep draining while non-periodic timers exist - return _timers.any((_FakeTimer timer) => !timer._isPeriodic); + // translation: drain every timer (periodic or not) that will occur up + // until the latest non-periodic timer + return _timers.any((_FakeTimer timer) => + !timer._isPeriodic || timer._nextCall <= _elapsed); } }); } @@ -233,8 +235,9 @@ class _FakeAsync implements FakeAsync { } _FakeTimer _getNextTimer() { - return min(_timers, - (timer1, timer2) => timer1._nextCall.compareTo(timer2._nextCall)); + return _timers.isEmpty + ? null + : _timers.reduce((t1, t2) => t1._nextCall <= t2._nextCall ? t1 : t2); } _runTimer(_FakeTimer timer) { diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 17209e620..bca558cb2 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -163,6 +163,46 @@ main() { }); }); + test('should call timers occurring at the same time in FIFO order', () { + new FakeAsync().run((async) { + var log = []; + new Timer(elapseBy ~/ 2, () { + log.add('1'); + }); + new Timer(elapseBy ~/ 2, () { + log.add('2'); + }); + async.elapse(elapseBy); + expect(log, ['1', '2']); + }); + }); + + test('should maintain FIFO order even with periodic timers', () { + new FakeAsync().run((async) { + var log = []; + new Timer.periodic(elapseBy ~/ 2, (_) { + log.add('periodic 1'); + }); + new Timer(elapseBy ~/ 2, () { + log.add('delayed 1'); + }); + new Timer(elapseBy, () { + log.add('delayed 2'); + }); + new Timer.periodic(elapseBy, (_) { + log.add('periodic 2'); + }); + async.elapse(elapseBy); + expect(log, [ + 'periodic 1', + 'delayed 1', + 'periodic 1', + 'delayed 2', + 'periodic 2' + ]); + }); + }); + test('should process microtasks surrounding each timer', () { new FakeAsync().run((async) { var microtaskCalls = 0; @@ -383,17 +423,17 @@ main() { }); group('flushTimers', () { - test('should flush timers', () { + test('should flush timers in FIFO order', () { new FakeAsync().run((async) { final log = []; new Future(() { - log.add(2); + log.add(1); new Future.delayed(elapseBy, () { log.add(3); }); }); new Future(() { - log.add(1); + log.add(2); }); expect(log, hasLength(0), reason: 'should not flush until asked to'); async.flushTimers(timeout: elapseBy * 2, flushPeriodicTimers: false); @@ -402,7 +442,9 @@ main() { }); }); - test('should run collateral periodic timers', () { + test( + 'should run collateral periodic timers with non-periodic first if ' + 'scheduled first', () { new FakeAsync().run((async) { final log = []; new Future.delayed(const Duration(seconds: 2), () { @@ -413,6 +455,23 @@ main() { }); expect(log, hasLength(0), reason: 'should not flush until asked to'); async.flushTimers(flushPeriodicTimers: false); + expect(log, ['periodic', 'delayed', 'periodic']); + }); + }); + + test( + 'should run collateral periodic timers with periodic first ' + 'if scheduled first', () { + new FakeAsync().run((async) { + final log = []; + new Timer.periodic(new Duration(seconds: 1), (_) { + log.add('periodic'); + }); + new Future.delayed(new Duration(seconds: 2), () { + log.add('delayed'); + }); + expect(log, hasLength(0), reason: 'should not flush until asked to'); + async.flushTimers(flushPeriodicTimers: false); expect(log, ['periodic', 'periodic', 'delayed']); }); }); From 3f459f26b9e0115072c6529f1a2c367542a30b69 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Wed, 3 Jan 2018 13:41:09 -0800 Subject: [PATCH 042/113] Fix USES_DYNAMIC_AS_BOTTOM errors (dart-lang/fake_async#407) --- pkgs/fake_async/test/fake_async_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index bca558cb2..7e90b02ff 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -358,7 +358,7 @@ main() { new FakeAsync().run((async) { var events = []; var errors = []; - var controller = new StreamController(); + var controller = new StreamController(); var timed = controller.stream.timeout(const Duration(minutes: 2)); var subscription = timed.listen(events.add, onError: errors.add); controller.add(0); From 336be945be1693d4a48bcddc023b1803c82519a2 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Wed, 3 Jan 2018 15:04:11 -0800 Subject: [PATCH 043/113] Update with Dart 2.0 core library unimplemented implementations (dart-lang/fake_async#406) --- pkgs/fake_async/lib/fake_async.dart | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index afa6ddae5..c25aad85b 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -285,4 +285,11 @@ class _FakeTimer implements Timer { bool get isActive => _time._hasTimer(this); cancel() => _time._cancelTimer(this); + + @override + // TODO: Dart 2.0 requires this method to be implemented. + // ignore: override_on_non_overriding_getter + int get tick { + throw new UnimplementedError("tick"); + } } From e27faf8291435b7c5c21238736e7bcff1d71da98 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Mar 2018 17:58:16 -0800 Subject: [PATCH 044/113] Add initial files for the standalone release of fake_async --- pkgs/fake_async/.gitignore | 5 + pkgs/fake_async/.travis.yml | 33 +++++ pkgs/fake_async/AUTHORS | 6 + pkgs/fake_async/CHANGELOG.md | 3 + pkgs/fake_async/CONTRIBUTING.md | 53 +++++++ pkgs/fake_async/LICENSE | 202 ++++++++++++++++++++++++++ pkgs/fake_async/README.md | 48 ++++++ pkgs/fake_async/analysis_options.yaml | 2 + pkgs/fake_async/pubspec.yaml | 8 + 9 files changed, 360 insertions(+) create mode 100644 pkgs/fake_async/.gitignore create mode 100644 pkgs/fake_async/.travis.yml create mode 100644 pkgs/fake_async/AUTHORS create mode 100644 pkgs/fake_async/CHANGELOG.md create mode 100644 pkgs/fake_async/CONTRIBUTING.md create mode 100644 pkgs/fake_async/LICENSE create mode 100644 pkgs/fake_async/README.md create mode 100644 pkgs/fake_async/analysis_options.yaml create mode 100644 pkgs/fake_async/pubspec.yaml diff --git a/pkgs/fake_async/.gitignore b/pkgs/fake_async/.gitignore new file mode 100644 index 000000000..63fe85d83 --- /dev/null +++ b/pkgs/fake_async/.gitignore @@ -0,0 +1,5 @@ +.packages +.pub/ +.dart_tool/ +build/ +pubspec.lock diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml new file mode 100644 index 000000000..9124e035b --- /dev/null +++ b/pkgs/fake_async/.travis.yml @@ -0,0 +1,33 @@ +language: dart + +# By default, builds are run in containers. +# https://docs.travis-ci.com/user/getting-started/#Selecting-infrastructure-(optional) +# Set `sudo: true` to disable containers if you need to use sudo in your scripts. +# sudo: true + +dart: +- dev +- stable + +# See https://docs.travis-ci.com/user/languages/dart/ for details. +dart_task: +- test: --platform vm +# Uncomment this line to run tests on the browser. +# - test: --platform firefox + +# Only run one instance of the formatter and the analyzer, rather than running +# them against each Dart version. +matrix: + include: + - dart: stable + dart_task: dartfmt + - dart: dev + dart_task: dartanalyzer + +# Only building master means that we don't run two builds for each pull request. +branches: + only: [master] + +cache: + directories: + - $HOME/.pub-cache diff --git a/pkgs/fake_async/AUTHORS b/pkgs/fake_async/AUTHORS new file mode 100644 index 000000000..e8063a8cd --- /dev/null +++ b/pkgs/fake_async/AUTHORS @@ -0,0 +1,6 @@ +# Below is a list of people and organizations that have contributed +# to the project. Names should be added to the list like so: +# +# Name/Organization + +Google Inc. diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md new file mode 100644 index 000000000..5b26ad30f --- /dev/null +++ b/pkgs/fake_async/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* Initial version. diff --git a/pkgs/fake_async/CONTRIBUTING.md b/pkgs/fake_async/CONTRIBUTING.md new file mode 100644 index 000000000..615b92ff9 --- /dev/null +++ b/pkgs/fake_async/CONTRIBUTING.md @@ -0,0 +1,53 @@ +Want to contribute? Great! First, read this page (including the small print at +the end). + +### Before you contribute + +Before we can use your code, you must sign the +[Google Individual Contributor License Agreement][CLA] (CLA), which you can do +online. The CLA is necessary mainly because you own the copyright to your +changes, even after your contribution becomes part of our codebase, so we need +your permission to use and distribute your code. We also need to be sure of +various other things—for instance that you'll tell us if you know that your code +infringes on other people's patents. You don't have to sign the CLA until after +you've submitted your code for review and a member has approved it, but you must +do it before we can put your code into our codebase. + +Before you start working on a larger contribution, you should get in touch with +us first through the issue tracker with your idea so that we can help out and +possibly guide you. Coordinating up front makes it much easier to avoid +frustration later on. + +[CLA]: https://cla.developers.google.com/about/google-individual + +### Code reviews + +All submissions, including submissions by project members, require review. We +recommend [forking the repository][fork], making changes in your fork, and +[sending us a pull request][pr] so we can review the changes and merge them into +this repository. + +[fork]: https://help.github.com/articles/about-forks/ +[pr]: https://help.github.com/articles/creating-a-pull-request/ + +Functional changes will require tests to be added or changed. The tests live in +the `test/` directory, and are run with `pub run test`. If you need to create +new tests, use the existing tests as a guideline for what they should look like. + +Before you send your pull request, make sure all the tests pass! + +### File headers + +All files in the project must start with the following header. + + // Copyright (c) 2017, 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. + +### The small print + +Contributions made by corporations are covered by a different agreement than the +one above, the +[Software Grant and Corporate Contributor License Agreement][CCLA]. + +[CCLA]: https://developers.google.com/open-source/cla/corporate diff --git a/pkgs/fake_async/LICENSE b/pkgs/fake_async/LICENSE new file mode 100644 index 000000000..7a4a3ea24 --- /dev/null +++ b/pkgs/fake_async/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. \ No newline at end of file diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md new file mode 100644 index 000000000..ea00b9b4c --- /dev/null +++ b/pkgs/fake_async/README.md @@ -0,0 +1,48 @@ +This package provides a [`FakeAsync`][] class, which makes it easy to +deterministically test code that uses asynchronous features like `Future`s, +`Stream`s, `Timer`s, and microtasks. It creates an environment in which the user +can explicitly control Dart's notion of the "current time". When the time is +advanced, `FakeAsync` fires all asynchronous events that are scheduled for that +time period without actually needing the test to wait for real time to elapse. + +[`FakeAsync`]: https://www.dartdocs.org/documentation/fake_async/latest/fake_async/FakeAsync-class.html + +For example: + +```dart +import 'dart:async'; + +import 'package:fake_async/fake_async.dart'; +import 'package:test/test.dart'; + +void main() { + test("Future.timeout() throws an error once the timeout is up", () { + // Any code run within [FakeAsync.run] is run within the context of that + // [FakeAsync] object. + new FakeAsync().run((async) { + // All asynchronous features that rely on timing are automatically + // controlled by [FakeAsync]. + expect(new Completer().future.timeout(new Duration(seconds: 5)), + throwsA(new isInstanceOf())); + + // This will cause the timeout above to fire immediately, without waiting + // 5 seconds of real time. + async.elapse(new Duration(seconds: 5)); + }); + }); +} +``` + +## Integration With `clock` + +`FakeAsync` can't control the time reported by [`new DateTime.now()`][] or by +the [`Stopwatch`][] class, since they're not part of `dart:async`. However, if +you create them using the [`clock`][] package's [`clock.now()`][] or +[`clock.getStopwatch()`][] functions, `FakeAsync` will automatically override +them to use the same notion of time as `dart:async` classes. + +[`new DateTime.now()`]: https://api.dartlang.org/stable/dart-core/DateTime/DateTime.now.html +[`Stopwatch`]: https://api.dartlang.org/stable/dart-core/Stopwatch-class.html +[`clock`]: https://pub.dartlang.org/packages/clock +[`clock.now`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html +[`clock.getStopwatch()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/getStopwatch.html diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml new file mode 100644 index 000000000..a10d4c5a0 --- /dev/null +++ b/pkgs/fake_async/analysis_options.yaml @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml new file mode 100644 index 000000000..91376cf55 --- /dev/null +++ b/pkgs/fake_async/pubspec.yaml @@ -0,0 +1,8 @@ +name: fake_async +version: 1.0.0-dev +description: Fake asynchronous events such as timers and microtasks for deterministic testing. +author: Dart Team +homepage: https://github.com/dart-lang/fake_async + +environment: + sdk: '>=1.24.0 <2.0.0' From 3cb8c17e42a681d6e7cf9e8aaafdfd4128616c44 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Mar 2018 20:18:25 -0800 Subject: [PATCH 045/113] Get the code running --- pkgs/fake_async/lib/fake_async.dart | 5 ++++- pkgs/fake_async/pubspec.yaml | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index c25aad85b..514a19de4 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -part of quiver.testing.async; +import 'dart:async'; +import 'dart:collection'; + +import 'package:quiver/time.dart'; /// A mechanism to make time-dependent units testable. /// diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 91376cf55..0d6702e45 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -6,3 +6,11 @@ homepage: https://github.com/dart-lang/fake_async environment: sdk: '>=1.24.0 <2.0.0' + +dependencies: + collection: '^1.8.0' + quiver: '^0.28.0' + +dev_dependencies: + test: '^0.12.28' + From 8de1b305c5c8261a96e53c5ae438ee47a97688ca Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Mar 2018 20:18:56 -0800 Subject: [PATCH 046/113] Style changes --- pkgs/fake_async/lib/fake_async.dart | 375 ++++------ pkgs/fake_async/pubspec.yaml | 1 + pkgs/fake_async/test/fake_async_test.dart | 875 ++++++++++------------ 3 files changed, 543 insertions(+), 708 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 514a19de4..7ffc82ac9 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -15,284 +15,231 @@ import 'dart:async'; import 'dart:collection'; +import 'package:collection/collection.dart'; import 'package:quiver/time.dart'; -/// A mechanism to make time-dependent units testable. +/// The type of a microtask callback. +typedef void _Microtask(); + +/// A class that mocks out the passage of time within a [Zone]. /// /// 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; - - /// 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]. - /// - /// Calls [callback] with `this` as argument and returns the result returned - /// by [callback]. - dynamic run(callback(FakeAsync self)); - - /// Runs all remaining microtasks, including those scheduled as a result of - /// running them, until there are no more microtasks scheduled. +/// The synchronous passage of time (as from blocking or expensive calls) can +/// also be simulated using [elapseBlocking]. +class FakeAsync { + /// The amount of fake time that's elapsed since this [FakeAsync] was + /// created. + var _elapsed = Duration.ZERO; + + /// The fake time at which the current call to [elapse] will finish running. /// - /// Does not run timers. - void flushMicrotasks(); + /// This is `null` if there's no current call to [elapse]. + Duration _elapsingTo; - /// 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 queue of microtasks that are scheduled to run when fake time + /// progresses. + final _microtasks = new Queue<_Microtask>(); - /// The number of created periodic timers that have not been canceled. - int get periodicTimerCount; + /// All timers created within [run]. + final _timers = new Set<_FakeTimer>(); - /// The number of pending non periodic timers that have not been canceled. - int get nonPeriodicTimerCount; + /// The number of active periodic timers created within a call to [run]. + int get periodicTimerCount => + _timers.where((timer) => timer._isPeriodic).length; - /// The number of pending microtasks. - int get microtaskCount; -} + /// The number of active non-periodic timers created within a call to [run]. + int get nonPeriodicTimerCount => + _timers.where((timer) => !timer._isPeriodic).length; -class _FakeAsync implements FakeAsync { - Duration _elapsed = Duration.ZERO; - Duration _elapsingTo; - Queue _microtasks = new Queue(); - Set<_FakeTimer> _timers = new Set<_FakeTimer>(); + /// The number of pending microtasks scheduled within a call to [run]. + int get microtaskCount => _microtasks.length; - @override + /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and + /// [elapseBlocking]. + /// + /// The returned clock starts at [initialTime] plus the fake time that's + /// already been elapsed. Further calls to [elapse] and [elapseBlocking] will + /// advance the clock as well. Clock getClock(DateTime initialTime) => new Clock(() => initialTime.add(_elapsed)); - @override + /// Simulates the asynchronous passage of time. + /// + /// Throws an [ArgumentError] if [duration] is negative. Throws a [StateError] + /// if a previous call to [elapse] has not yet completed. + /// + /// Any timers created within [run] will fire if their time is within + /// [duration]. The microtask queue is processed before and after each timer + /// fires. void elapse(Duration duration) { if (duration.inMicroseconds < 0) { - throw new ArgumentError('Cannot call elapse with negative duration'); - } - if (_elapsingTo != null) { + throw new ArgumentError.value( + duration, 'duration', 'may not be negative'); + } else if (_elapsingTo != null) { throw new StateError('Cannot elapse until previous elapse is complete.'); } + _elapsingTo = _elapsed + duration; - _drainTimersWhile((_FakeTimer next) => next._nextCall <= _elapsingTo); + _fireTimersWhile((next) => next._nextCall <= _elapsingTo); _elapseTo(_elapsingTo); _elapsingTo = null; } - @override + /// Simulates the synchronous passage of time, resulting from blocking or + /// expensive calls. + /// + /// Neither timers nor microtasks are run during this call, but if this is + /// called within [elapse] they may fire afterwards. + /// + /// Throws an [ArgumentError] if [duration] is negative. 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; - } + if (_elapsingTo != null && _elapsed > _elapsingTo) _elapsingTo = _elapsed; } - @override + /// Runs [callback] in a [Zone] where all asynchrony is controlled by [this]. + /// + /// All [Future]s, [Stream]s, [Timer]s, microtasks, and other time-based + /// asynchronous features used within [callback] are controlled by calls to + /// [elapse] rather than the passing of real time. + /// + /// Calls [callback] with `this` as argument and returns its result. + T run(T callback(FakeAsync self)) => runZoned(() => callback(this), + zoneSpecification: new ZoneSpecification( + createTimer: (_, __, ___, duration, callback) => + _createTimer(duration, callback, false), + createPeriodicTimer: (_, __, ___, duration, callback) => + _createTimer(duration, callback, true), + scheduleMicrotask: (_, __, ___, microtask) => + _microtasks.add(microtask))); + + /// Runs all pending microtasks scheduled within a call to [run] until there + /// are no more microtasks scheduled. + /// + /// Does not run timers. void flushMicrotasks() { - _drainMicrotasks(); + while (_microtasks.isNotEmpty) { + _microtasks.removeFirst()(); + } } - @override - void flushTimers( - {Duration timeout: const Duration(hours: 1), - bool flushPeriodicTimers: true}) { - final absoluteTimeout = _elapsed + timeout; - _drainTimersWhile((_FakeTimer timer) { + /// Elapses time until there are no more active timers. + /// + /// If `flushPeriodicTimers` is `true` (the default), this will repeatedly run + /// periodic timers until they're explicitly canceled. Otherwise, this will + /// stop when the only active timers are periodic. + /// + /// The [timeout] controls how much fake time may elapse before a [StateError] + /// is thrown. This ensures that a periodic timer doesn't cause this method to + /// deadlock. It defaults to one hour. + void flushTimers({Duration timeout, bool flushPeriodicTimers: true}) { + timeout ??= const Duration(hours: 1); + + var absoluteTimeout = _elapsed + timeout; + _fireTimersWhile((timer) { if (timer._nextCall > absoluteTimeout) { + // TODO(nweiz): Make this a [TimeoutException]. throw new StateError( 'Exceeded timeout ${timeout} while flushing timers'); } - if (flushPeriodicTimers) { - return _timers.isNotEmpty; - } else { - // translation: drain every timer (periodic or not) that will occur up - // until the latest non-periodic timer - return _timers.any((_FakeTimer timer) => - !timer._isPeriodic || timer._nextCall <= _elapsed); - } - }); - } - @override - run(callback(FakeAsync self)) { - if (_zone == null) { - _zone = Zone.current.fork(specification: _zoneSpec); - } - var result; - _zone.runGuarded(() { - result = callback(this); - }); - return result; - } + if (flushPeriodicTimers) return _timers.isNotEmpty; - 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 timer)) { - _drainMicrotasks(); - _FakeTimer next; - while ((next = _getNextTimer()) != null && predicate(next)) { - _runTimer(next); - _drainMicrotasks(); - } + // Continue firing timers until the only ones left are periodic *and* + // every periodic timer has had a change to run against the final + // value of [_elapsed]. + return _timers + .any((timer) => !timer._isPeriodic || timer._nextCall <= _elapsed); + }); } - _elapseTo(Duration to) { - if (to > _elapsed) { - _elapsed = to; + /// Invoke the callback for each timer until [predicate] returns `false` for + /// the next timer that would be fired. + /// + /// Microtasks are flushed before and after each timer is fired. Before each + /// timer fires, [_elapsed] is updated to the appropriate duration. + void _fireTimersWhile(bool predicate(_FakeTimer timer)) { + flushMicrotasks(); + while (true) { + if (_timers.isEmpty) break; + + var timer = minBy(_timers, (timer) => timer._nextCall); + if (!predicate(timer)) break; + + _elapseTo(timer._nextCall); + timer._fire(); + flushMicrotasks(); } } - Timer _createTimer(Duration duration, Function callback, bool isPeriodic) { - var timer = new _FakeTimer._(duration, callback, isPeriodic, this); + /// Creates a new timer controlled by [this] that fires [callback] after + /// [duration] (or every [duration] if [periodic] is `true`). + Timer _createTimer(Duration duration, Function callback, bool periodic) { + var timer = new _FakeTimer(duration, callback, periodic, this); _timers.add(timer); return timer; } - _FakeTimer _getNextTimer() { - return _timers.isEmpty - ? null - : _timers.reduce((t1, t2) => t1._nextCall <= t2._nextCall ? t1 : t2); + /// Sets [_elapsed] to [to] if [to] is longer than [_elapsed]. + void _elapseTo(Duration to) { + if (to > _elapsed) _elapsed = to; } - - _runTimer(_FakeTimer timer) { - assert(timer.isActive); - _elapseTo(timer._nextCall); - if (timer._isPeriodic) { - timer._callback(timer); - timer._nextCall += timer._duration; - } else { - _timers.remove(timer); - timer._callback(); - } - } - - _drainMicrotasks() { - while (_microtasks.isNotEmpty) { - _microtasks.removeFirst()(); - } - } - - _hasTimer(_FakeTimer timer) => _timers.contains(timer); - - _cancelTimer(_FakeTimer timer) => _timers.remove(timer); } +/// An implementation of [Timer] that's controlled by a [FakeAsync]. class _FakeTimer implements Timer { + /// If this is periodic, the time that should elapse between firings of this + /// timer. + /// + /// This is not used by non-periodic timers. final Duration _duration; + + /// The callback to invoke when the timer fires. + /// + /// For periodic timers, this is a `void Function(Timer)`. For non-periodic + /// timers, it's a `void Function()`. final Function _callback; + + /// Whether this is a periodic timer. final bool _isPeriodic; - final _FakeAsync _time; + + /// The [FakeAsync] instance that controls this timer. + final FakeAsync _async; + + /// The value of [FakeAsync._elapsed] at (or after) which this timer should be + /// fired. 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; + // TODO: Dart 2.0 requires this method to be implemented. + int get tick { + throw new UnimplementedError("tick"); + } - _FakeTimer._(Duration duration, this._callback, this._isPeriodic, this._time) - : _duration = duration < _minDuration ? _minDuration : duration { - _nextCall = _time._elapsed + _duration; + _FakeTimer(Duration duration, this._callback, this._isPeriodic, this._async) + : _duration = duration < Duration.ZERO ? Duration.ZERO : duration { + _nextCall = _async._elapsed + _duration; } - bool get isActive => _time._hasTimer(this); + bool get isActive => _async._timers.contains(this); - cancel() => _time._cancelTimer(this); + void cancel() => _async._timers.remove(this); - @override - // TODO: Dart 2.0 requires this method to be implemented. - // ignore: override_on_non_overriding_getter - int get tick { - throw new UnimplementedError("tick"); + /// Fires this timer's callback and updates its state as necessary. + void _fire() { + assert(isActive); + if (_isPeriodic) { + _callback(this); + _nextCall += _duration; + } else { + cancel(); + _callback(); + } } } diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 0d6702e45..1d43a7b9a 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -12,5 +12,6 @@ dependencies: quiver: '^0.28.0' dev_dependencies: + async: '>=1.10.0 <3.0.0' test: '^0.12.28' diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 7e90b02ff..2268f2abd 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -12,614 +12,501 @@ // See the License for the specific language governing permissions and // limitations under the License. -library quiver.testing.async.fake_async_test; - import 'dart:async'; -import 'package:quiver/testing/async.dart'; +import 'package:fake_async/fake_async.dart'; + import 'package:test/test.dart'; main() { - group('FakeAsync', () { - var initialTime = new DateTime(2000); - var elapseBy = const Duration(days: 1); + var initialTime = new DateTime(2000); + var elapseBy = new Duration(days: 1); + + test('should set initial time', () { + expect(new FakeAsync().getClock(initialTime).now(), initialTime); + }); - test('should set initial time', () { - expect(new FakeAsync().getClock(initialTime).now(), initialTime); + group('elapseBlocking', () { + test('should elapse time without calling timers', () { + new Timer(elapseBy ~/ 2, neverCalled); + new FakeAsync().elapseBlocking(elapseBy); }); - group('elapseBlocking', () { - test('should elapse time without calling timers', () { - var timerCalled = false; - var timer = new Timer(elapseBy ~/ 2, () => timerCalled = true); - new FakeAsync().elapseBlocking(elapseBy); - expect(timerCalled, isFalse); - timer.cancel(); - }); + test('should elapse time by the specified amount', () { + var async = new FakeAsync(); + async.elapseBlocking(elapseBy); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + }); - test('should elapse time by the specified amount', () { - var it = new FakeAsync(); - it.elapseBlocking(elapseBy); - expect(it.getClock(initialTime).now(), initialTime.add(elapseBy)); + test('should throw when called with a negative duration', () { + expect(() => new FakeAsync().elapseBlocking(new Duration(days: -1)), + throwsArgumentError); + }); + }); + + group('elapse', () { + test('should elapse time by the specified amount', () { + new FakeAsync().run((async) { + async.elapse(elapseBy); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); }); + }); + + test('should throw ArgumentError when called with a negative duration', () { + expect(() => new FakeAsync().elapse(new Duration(days: -1)), + throwsArgumentError); + }); - test('should throw when called with a negative duration', () { - expect(() { - new FakeAsync().elapseBlocking(const Duration(days: -1)); - }, throwsA(new isInstanceOf())); + test('should throw when called before previous call is complete', () { + new FakeAsync().run((async) { + new Timer(elapseBy ~/ 2, expectAsync0(() { + expect(() => async.elapse(elapseBy), throwsStateError); + })); + async.elapse(elapseBy); }); }); - group('elapse', () { - test('should elapse time by the specified amount', () { + group('when creating timers', () { + test('should call timers expiring before or at end time', () { new FakeAsync().run((async) { + new Timer(elapseBy ~/ 2, expectAsync0(() {})); + new Timer(elapseBy, expectAsync0(() {})); async.elapse(elapseBy); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); }); }); - test('should throw ArgumentError when called with a negative duration', - () { - expect(() => new FakeAsync().elapse(const Duration(days: -1)), - throwsA(new isInstanceOf())); - }); - - test('should throw when called before previous call is complete', () { + test('should call timers expiring due to elapseBlocking', () { new FakeAsync().run((async) { - var error; - new Timer(elapseBy ~/ 2, () { - try { - async.elapse(elapseBy); - } catch (e) { - error = e; - } - }); + new Timer(elapseBy, () => async.elapseBlocking(elapseBy)); + new Timer(elapseBy * 2, expectAsync0(() {})); async.elapse(elapseBy); - expect(error, new isInstanceOf()); + expect( + async.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); }); }); - group('when creating timers', () { - test('should call timers expiring before or at end time', () { - new FakeAsync().run((async) { - var beforeCallCount = 0; - var atCallCount = 0; - new Timer(elapseBy ~/ 2, () { - beforeCallCount++; - }); - new Timer(elapseBy, () { - atCallCount++; - }); - async.elapse(elapseBy); - expect(beforeCallCount, 1); - expect(atCallCount, 1); - }); - }); - - test('should call timers expiring due to elapseBlocking', () { - new FakeAsync().run((async) { - bool secondaryCalled = false; - new Timer(elapseBy, () { - async.elapseBlocking(elapseBy); - }); - new Timer(elapseBy * 2, () { - secondaryCalled = true; - }); - async.elapse(elapseBy); - expect(secondaryCalled, isTrue); + test('should call timers at their scheduled time', () { + new FakeAsync().run((async) { + new Timer(elapseBy ~/ 2, expectAsync0(() { expect(async.getClock(initialTime).now(), - initialTime.add(elapseBy * 2)); - }); - }); + initialTime.add(elapseBy ~/ 2)); + })); - test('should call timers at their scheduled time', () { - new FakeAsync().run((async) { - DateTime calledAt; - var periodicCalledAt = []; - new Timer(elapseBy ~/ 2, () { - calledAt = async.getClock(initialTime).now(); - }); - new Timer.periodic(elapseBy ~/ 2, (_) { - periodicCalledAt.add(async.getClock(initialTime).now()); - }); - async.elapse(elapseBy); - expect(calledAt, initialTime.add(elapseBy ~/ 2)); - expect(periodicCalledAt, - [elapseBy ~/ 2, elapseBy].map(initialTime.add)); - }); - }); + var periodicCalledAt = []; + new Timer.periodic(elapseBy ~/ 2, + (_) => periodicCalledAt.add(async.getClock(initialTime).now())); - test('should not call timers expiring after end time', () { - new FakeAsync().run((async) { - var timerCallCount = 0; - new Timer(elapseBy * 2, () { - timerCallCount++; - }); - async.elapse(elapseBy); - expect(timerCallCount, 0); - }); + async.elapse(elapseBy); + expect( + periodicCalledAt, [elapseBy ~/ 2, elapseBy].map(initialTime.add)); }); + }); - test('should not call canceled timers', () { - new FakeAsync().run((async) { - int timerCallCount = 0; - var timer = new Timer(elapseBy ~/ 2, () { - timerCallCount++; - }); - timer.cancel(); - async.elapse(elapseBy); - expect(timerCallCount, 0); - }); + test('should not call timers expiring after end time', () { + new FakeAsync().run((async) { + new Timer(elapseBy * 2, neverCalled); + async.elapse(elapseBy); }); + }); - test('should call periodic timers each time the duration elapses', () { - new FakeAsync().run((async) { - var periodicCallCount = 0; - new Timer.periodic(elapseBy ~/ 10, (_) { - periodicCallCount++; - }); - async.elapse(elapseBy); - expect(periodicCallCount, 10); - }); + test('should not call canceled timers', () { + new FakeAsync().run((async) { + var timer = new Timer(elapseBy ~/ 2, neverCalled); + timer.cancel(); + async.elapse(elapseBy); }); + }); - test('should call timers occurring at the same time in FIFO order', () { - new FakeAsync().run((async) { - var log = []; - new Timer(elapseBy ~/ 2, () { - log.add('1'); - }); - new Timer(elapseBy ~/ 2, () { - log.add('2'); - }); - async.elapse(elapseBy); - expect(log, ['1', '2']); - }); + test('should call periodic timers each time the duration elapses', () { + new FakeAsync().run((async) { + new Timer.periodic(elapseBy ~/ 10, expectAsync1((_) {}, count: 10)); + async.elapse(elapseBy); }); + }); - test('should maintain FIFO order even with periodic timers', () { - new FakeAsync().run((async) { - var log = []; - new Timer.periodic(elapseBy ~/ 2, (_) { - log.add('periodic 1'); - }); - new Timer(elapseBy ~/ 2, () { - log.add('delayed 1'); - }); - new Timer(elapseBy, () { - log.add('delayed 2'); - }); - new Timer.periodic(elapseBy, (_) { - log.add('periodic 2'); - }); - async.elapse(elapseBy); - expect(log, [ - 'periodic 1', - 'delayed 1', - 'periodic 1', - 'delayed 2', - 'periodic 2' - ]); - }); + test('should call timers occurring at the same time in FIFO order', () { + new FakeAsync().run((async) { + var log = []; + new Timer(elapseBy ~/ 2, () => log.add('1')); + new Timer(elapseBy ~/ 2, () => log.add('2')); + async.elapse(elapseBy); + expect(log, ['1', '2']); }); + }); - test('should process microtasks surrounding each timer', () { - new FakeAsync().run((async) { - var microtaskCalls = 0; - var timerCalls = 0; - scheduleMicrotasks() { - for (int i = 0; i < 5; i++) { - scheduleMicrotask(() => microtaskCalls++); - } - } + test('should maintain FIFO order even with periodic timers', () { + new FakeAsync().run((async) { + var log = []; + new Timer.periodic(elapseBy ~/ 2, (_) => log.add('periodic 1')); + new Timer(elapseBy ~/ 2, () => log.add('delayed 1')); + new Timer(elapseBy, () => log.add('delayed 2')); + new Timer.periodic(elapseBy, (_) => log.add('periodic 2')); - scheduleMicrotasks(); - new Timer.periodic(elapseBy ~/ 5, (_) { - timerCalls++; - expect(microtaskCalls, 5 * timerCalls); - scheduleMicrotasks(); - }); - async.elapse(elapseBy); - expect(timerCalls, 5); - expect(microtaskCalls, 5 * (timerCalls + 1)); - }); + async.elapse(elapseBy); + expect(log, [ + 'periodic 1', + 'delayed 1', + 'periodic 1', + 'delayed 2', + 'periodic 2' + ]); }); + }); - test('should pass the periodic timer itself to callbacks', () { - new FakeAsync().run((async) { - Timer passedTimer; - Timer periodic = new Timer.periodic(elapseBy, (timer) { - passedTimer = timer; - }); - async.elapse(elapseBy); - expect(periodic, same(passedTimer)); - }); - }); + test('should process microtasks surrounding each timer', () { + new FakeAsync().run((async) { + var microtaskCalls = 0; + var timerCalls = 0; + scheduleMicrotasks() { + for (var i = 0; i < 5; i++) { + scheduleMicrotask(() => microtaskCalls++); + } + } - test('should call microtasks before advancing time', () { - new FakeAsync().run((async) { - DateTime calledAt; - scheduleMicrotask(() { - calledAt = async.getClock(initialTime).now(); - }); - async.elapse(const Duration(minutes: 1)); - expect(calledAt, initialTime); + scheduleMicrotasks(); + new Timer.periodic(elapseBy ~/ 5, (_) { + timerCalls++; + expect(microtaskCalls, 5 * timerCalls); + scheduleMicrotasks(); }); + async.elapse(elapseBy); + expect(timerCalls, 5); + expect(microtaskCalls, 5 * (timerCalls + 1)); }); + }); - test('should add event before advancing time', () { - return new Future(() => new FakeAsync().run((async) { - var controller = new StreamController(); - var ret = controller.stream.first.then((_) { - expect(async.getClock(initialTime).now(), initialTime); - }); - controller.add(null); - async.elapse(const Duration(minutes: 1)); - return ret; - })); + test('should pass the periodic timer itself to callbacks', () { + new FakeAsync().run((async) { + Timer constructed; + constructed = new Timer.periodic(elapseBy, expectAsync1((passed) { + expect(passed, same(constructed)); + })); + async.elapse(elapseBy); }); + }); - test('should increase negative duration timers to zero duration', () { - new FakeAsync().run((async) { - var negativeDuration = const Duration(days: -1); - DateTime calledAt; - new Timer(negativeDuration, () { - calledAt = async.getClock(initialTime).now(); - }); - async.elapse(const Duration(minutes: 1)); - expect(calledAt, initialTime); - }); + test('should call microtasks before advancing time', () { + new FakeAsync().run((async) { + scheduleMicrotask(expectAsync0(() { + expect(async.getClock(initialTime).now(), initialTime); + })); + async.elapse(new Duration(minutes: 1)); }); + }); - test('should not be additive with elapseBlocking', () { - new FakeAsync().run((async) { - new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); - async.elapse(elapseBy); - expect(async.getClock(initialTime).now(), - initialTime.add(elapseBy * 5)); - }); + test('should add event before advancing time', () { + new FakeAsync().run((async) { + var controller = new StreamController(); + expect(controller.stream.first.then((_) { + expect(async.getClock(initialTime).now(), initialTime); + }), completes); + controller.add(null); + async.elapse(new Duration(minutes: 1)); }); + }); - group('isActive', () { - test('should be false after timer is run', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, () {}); - async.elapse(elapseBy); - expect(timer.isActive, isFalse); - }); - }); - - test('should be true after periodic timer is run', () { - new FakeAsync().run((async) { - var timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); - async.elapse(elapseBy); - expect(timer.isActive, isTrue); - }); - }); - - test('should be false after timer is canceled', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, () {}); - timer.cancel(); - expect(timer.isActive, isFalse); - }); - }); + test('should increase negative duration timers to zero duration', () { + new FakeAsync().run((async) { + var negativeDuration = new Duration(days: -1); + new Timer(negativeDuration, expectAsync0(() { + expect(async.getClock(initialTime).now(), initialTime); + })); + async.elapse(new Duration(minutes: 1)); }); + }); - test('should work with new Future()', () { - new FakeAsync().run((async) { - var callCount = 0; - new Future(() => callCount++); - async.elapse(Duration.ZERO); - expect(callCount, 1); - }); + test('should not be additive with elapseBlocking', () { + new FakeAsync().run((async) { + new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); + async.elapse(elapseBy); + expect( + async.getClock(initialTime).now(), initialTime.add(elapseBy * 5)); }); + }); - test('should work with Future.delayed', () { + group('isActive', () { + test('should be false after timer is run', () { new FakeAsync().run((async) { - int result; - new Future.delayed(elapseBy, () => result = 5); + var timer = new Timer(elapseBy ~/ 2, () {}); async.elapse(elapseBy); - expect(result, 5); + expect(timer.isActive, isFalse); }); }); - test('should work with Future.timeout', () { + test('should be true after periodic timer is run', () { new FakeAsync().run((async) { - var completer = new Completer(); - TimeoutException timeout; - completer.future.timeout(elapseBy ~/ 2).catchError((err) { - timeout = err; - }); + var timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); async.elapse(elapseBy); - expect(timeout, new isInstanceOf()); - completer.complete(); + expect(timer.isActive, isTrue); }); }); - // TODO: Pausing and resuming the timeout Stream doesn't work since - // it uses `new Stopwatch()`. - // - // See https://code.google.com/p/dart/issues/detail?id=18149 - test('should work with Stream.periodic', () { + test('should be false after timer is canceled', () { new FakeAsync().run((async) { - var events = []; - StreamSubscription subscription; - var periodic = - new Stream.periodic(const Duration(minutes: 1), (i) => i); - subscription = periodic.listen(events.add); - async.elapse(const Duration(minutes: 3)); - expect(events, [0, 1, 2]); - subscription.cancel(); - }); - }); - - test('should work with Stream.timeout', () { - new FakeAsync().run((async) { - var events = []; - var errors = []; - var controller = new StreamController(); - var timed = controller.stream.timeout(const Duration(minutes: 2)); - var subscription = timed.listen(events.add, onError: errors.add); - controller.add(0); - async.elapse(const Duration(minutes: 1)); - expect(events, [0]); - async.elapse(const Duration(minutes: 1)); - expect(errors, hasLength(1)); - expect(errors.first, new isInstanceOf()); - subscription.cancel(); - controller.close(); + var timer = new Timer(elapseBy ~/ 2, () {}); + timer.cancel(); + expect(timer.isActive, isFalse); }); }); }); - }); - group('flushMicrotasks', () { - test('should flush a microtask', () { + test('should work with new Future()', () { new FakeAsync().run((async) { - bool microtaskRan = false; - new Future.microtask(() { - microtaskRan = true; - }); - expect(microtaskRan, isFalse, - reason: 'should not flush until asked to'); - async.flushMicrotasks(); - expect(microtaskRan, isTrue); + new Future(expectAsync0(() {})); + async.elapse(Duration.ZERO); }); }); - test('should flush microtasks scheduled by microtasks in order', () { + + test('should work with Future.delayed', () { new FakeAsync().run((async) { - final log = []; - new Future.microtask(() { - log.add(1); - new Future.microtask(() { - log.add(3); - }); - }); - new Future.microtask(() { - log.add(2); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushMicrotasks(); - expect(log, [1, 2, 3]); + new Future.delayed(elapseBy, expectAsync0(() {})); + async.elapse(elapseBy); }); }); - test('should not run timers', () { + + test('should work with Future.timeout', () { new FakeAsync().run((async) { - final log = []; - new Future.microtask(() { - log.add(1); - }); - new Future(() { - log.add(2); - }); - new Timer.periodic(const Duration(seconds: 1), (_) { - log.add(2); - }); - async.flushMicrotasks(); - expect(log, [1]); + var completer = new Completer(); + expect(completer.future.timeout(elapseBy ~/ 2), + throwsA(new isInstanceOf())); + async.elapse(elapseBy); + completer.complete(); }); }); - }); - group('flushTimers', () { - test('should flush timers in FIFO order', () { + // TODO: Pausing and resuming the timeout Stream doesn't work since + // it uses `new Stopwatch()`. + // + // See https://code.google.com/p/dart/issues/detail?id=18149 + test('should work with Stream.periodic', () { new FakeAsync().run((async) { - final log = []; - new Future(() { - log.add(1); - new Future.delayed(elapseBy, () { - log.add(3); - }); - }); - new Future(() { - log.add(2); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushTimers(timeout: elapseBy * 2, flushPeriodicTimers: false); - expect(log, [1, 2, 3]); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + expect(new Stream.periodic(new Duration(minutes: 1), (i) => i), + emitsInOrder([0, 1, 2])); + async.elapse(new Duration(minutes: 3)); }); }); - test( - 'should run collateral periodic timers with non-periodic first if ' - 'scheduled first', () { + test('should work with Stream.timeout', () { new FakeAsync().run((async) { - final log = []; - new Future.delayed(const Duration(seconds: 2), () { - log.add('delayed'); - }); - new Timer.periodic(const Duration(seconds: 1), (_) { - log.add('periodic'); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushTimers(flushPeriodicTimers: false); - expect(log, ['periodic', 'delayed', 'periodic']); + var controller = new StreamController(); + var timed = controller.stream.timeout(new Duration(minutes: 2)); + + var events = []; + var errors = []; + timed.listen(events.add, onError: errors.add); + + controller.add(0); + async.elapse(new Duration(minutes: 1)); + expect(events, [0]); + + async.elapse(new Duration(minutes: 1)); + expect(errors, hasLength(1)); + expect(errors.first, new isInstanceOf()); }); }); + }); + }); - test( - 'should run collateral periodic timers with periodic first ' - 'if scheduled first', () { - new FakeAsync().run((async) { - final log = []; - new Timer.periodic(new Duration(seconds: 1), (_) { - log.add('periodic'); - }); - new Future.delayed(new Duration(seconds: 2), () { - log.add('delayed'); - }); - expect(log, hasLength(0), reason: 'should not flush until asked to'); - async.flushTimers(flushPeriodicTimers: false); - expect(log, ['periodic', 'periodic', 'delayed']); - }); + group('flushMicrotasks', () { + test('should flush a microtask', () { + new FakeAsync().run((async) { + new Future.microtask(expectAsync0(() {})); + async.flushMicrotasks(); }); + }); - test('should timeout', () { - new FakeAsync().run((async) { - int count = 0; - // Schedule 3 timers. All but the last one should fire. - for (int delay in [30, 60, 90]) { - new Future.delayed(new Duration(minutes: delay), () { - count++; - }); - } - expect(() => async.flushTimers(flushPeriodicTimers: false), - throwsStateError); - expect(count, 2); + test('should flush microtasks scheduled by microtasks in order', () { + new FakeAsync().run((async) { + var log = []; + scheduleMicrotask(() { + log.add(1); + scheduleMicrotask(() => log.add(3)); }); + scheduleMicrotask(() => log.add(2)); + + async.flushMicrotasks(); + expect(log, [1, 2, 3]); }); + }); - test('should timeout a chain of timers', () { - new FakeAsync().run((async) { - int count = 0; - createTimer() { - new Future.delayed(const Duration(minutes: 30), () { - count++; - createTimer(); - }); - } + test('should not run timers', () { + new FakeAsync().run((async) { + var log = []; + scheduleMicrotask(() => log.add(1)); + Timer.run(() => log.add(2)); + new Timer.periodic(new Duration(seconds: 1), (_) => log.add(2)); - createTimer(); - expect( - () => async.flushTimers( - timeout: const Duration(hours: 2), - flushPeriodicTimers: false), - throwsStateError); - expect(count, 4); - }); + async.flushMicrotasks(); + expect(log, [1]); }); + }); + }); - test('should timeout periodic timers', () { - new FakeAsync().run((async) { - int count = 0; - new Timer.periodic(const Duration(minutes: 30), (Timer timer) { - count++; - }); - expect(() => async.flushTimers(timeout: const Duration(hours: 1)), - throwsStateError); - expect(count, 2); + group('flushTimers', () { + test('should flush timers in FIFO order', () { + new FakeAsync().run((async) { + var log = []; + Timer.run(() { + log.add(1); + new Timer(elapseBy, () => log.add(3)); }); + Timer.run(() => log.add(2)); + + async.flushTimers(timeout: elapseBy * 2); + expect(log, [1, 2, 3]); + expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); }); + }); - test('should flush periodic timers', () { - new FakeAsync().run((async) { - int count = 0; - new Timer.periodic(const Duration(minutes: 30), (Timer timer) { - if (count == 3) { - timer.cancel(); - } + test( + 'should run collateral periodic timers with non-periodic first if ' + 'scheduled first', () { + new FakeAsync().run((async) { + var log = []; + new Timer(new Duration(seconds: 2), () => log.add('delayed')); + new Timer.periodic( + new Duration(seconds: 1), (_) => log.add('periodic')); + + async.flushTimers(flushPeriodicTimers: false); + expect(log, ['periodic', 'delayed', 'periodic']); + }); + }); + + test( + 'should run collateral periodic timers with periodic first ' + 'if scheduled first', () { + new FakeAsync().run((async) { + var log = []; + new Timer.periodic( + new Duration(seconds: 1), (_) => log.add('periodic')); + new Timer(new Duration(seconds: 2), () => log.add('delayed')); + + async.flushTimers(flushPeriodicTimers: false); + expect(log, ['periodic', 'periodic', 'delayed']); + }); + }); + + test('should time out', () { + new FakeAsync().run((async) { + // Schedule 3 timers. All but the last one should fire. + for (var delay in [30, 60, 90]) { + new Timer(new Duration(minutes: delay), + expectAsync0(() {}, count: delay == 90 ? 0 : 1)); + } + + expect(() => async.flushTimers(), throwsStateError); + }); + }); + + test('should time out a chain of timers', () { + new FakeAsync().run((async) { + var count = 0; + createTimer() { + new Timer(new Duration(minutes: 30), () { count++; + createTimer(); }); - async.flushTimers(timeout: const Duration(hours: 20)); - expect(count, 4); - }); + } + + createTimer(); + expect(() => async.flushTimers(timeout: new Duration(hours: 2)), + throwsStateError); + expect(count, 4); }); + }); - test('should compute absolute timeout as elapsed + timeout', () { - new FakeAsync().run((async) { - final log = []; - int count = 0; - createTimer() { - new Future.delayed(const Duration(minutes: 30), () { - log.add(count); - count++; - if (count < 4) { - createTimer(); - } - }); - } + test('should time out periodic timers', () { + new FakeAsync().run((async) { + new Timer.periodic( + new Duration(minutes: 30), expectAsync1((_) {}, count: 2)); + expect(() => async.flushTimers(timeout: new Duration(hours: 1)), + throwsStateError); + }); + }); - createTimer(); - async.elapse(const Duration(hours: 1)); - async.flushTimers(timeout: const Duration(hours: 1)); - expect(count, 4); + test('should flush periodic timers', () { + new FakeAsync().run((async) { + var count = 0; + new Timer.periodic(new Duration(minutes: 30), (timer) { + if (count == 3) timer.cancel(); + count++; }); + async.flushTimers(timeout: new Duration(hours: 20)); + expect(count, 4); }); }); - group('stats', () { - test('should report the number of pending microtasks', () { - new FakeAsync().run((async) { - expect(async.microtaskCount, 0); - scheduleMicrotask(() => null); - expect(async.microtaskCount, 1); - scheduleMicrotask(() => null); - expect(async.microtaskCount, 2); - async.flushMicrotasks(); - expect(async.microtaskCount, 0); - }); + test('should compute absolute timeout as elapsed + timeout', () { + new FakeAsync().run((async) { + var count = 0; + createTimer() { + new Timer(new Duration(minutes: 30), () { + count++; + if (count < 4) createTimer(); + }); + } + + createTimer(); + async.elapse(new Duration(hours: 1)); + async.flushTimers(timeout: new Duration(hours: 1)); + expect(count, 4); }); + }); + }); - test('it should report the number of pending periodic timers', () { - new FakeAsync().run((async) { - expect(async.periodicTimerCount, 0); - Timer timer = - new Timer.periodic(const Duration(minutes: 30), (Timer timer) {}); - expect(async.periodicTimerCount, 1); - new Timer.periodic(const Duration(minutes: 20), (Timer timer) {}); - expect(async.periodicTimerCount, 2); - async.elapse(const Duration(minutes: 20)); - expect(async.periodicTimerCount, 2); - timer.cancel(); - expect(async.periodicTimerCount, 1); - }); + group('stats', () { + test('should report the number of pending microtasks', () { + new FakeAsync().run((async) { + expect(async.microtaskCount, 0); + scheduleMicrotask(() => null); + expect(async.microtaskCount, 1); + scheduleMicrotask(() => null); + expect(async.microtaskCount, 2); + async.flushMicrotasks(); + expect(async.microtaskCount, 0); }); + }); - test('it should report the number of pending non periodic timers', () { - new FakeAsync().run((async) { - expect(async.nonPeriodicTimerCount, 0); - Timer timer = new Timer(const Duration(minutes: 30), () {}); - expect(async.nonPeriodicTimerCount, 1); - new Timer(const Duration(minutes: 20), () {}); - expect(async.nonPeriodicTimerCount, 2); - async.elapse(const Duration(minutes: 25)); - expect(async.nonPeriodicTimerCount, 1); - timer.cancel(); - expect(async.nonPeriodicTimerCount, 0); - }); + test('it should report the number of pending periodic timers', () { + new FakeAsync().run((async) { + expect(async.periodicTimerCount, 0); + var timer = new Timer.periodic(new Duration(minutes: 30), (_) {}); + expect(async.periodicTimerCount, 1); + new Timer.periodic(new Duration(minutes: 20), (_) {}); + expect(async.periodicTimerCount, 2); + async.elapse(new Duration(minutes: 20)); + expect(async.periodicTimerCount, 2); + timer.cancel(); + expect(async.periodicTimerCount, 1); }); }); - group('timers', () { - test('should behave like real timers', () { - return new FakeAsync().run((async) { - var timeout = const Duration(minutes: 1); - int counter = 0; - var timer; - timer = new Timer(timeout, () { - counter++; - expect(timer.isActive, isFalse, - reason: "is not active while executing callback"); - }); - expect(timer.isActive, isTrue, - reason: "is active before executing callback"); - async.elapse(timeout); - expect(counter, equals(1), reason: "timer executed"); - expect(timer.isActive, isFalse, - reason: "is not active after executing callback"); - }); + test('it should report the number of pending non periodic timers', () { + new FakeAsync().run((async) { + expect(async.nonPeriodicTimerCount, 0); + Timer timer = new Timer(new Duration(minutes: 30), () {}); + expect(async.nonPeriodicTimerCount, 1); + new Timer(new Duration(minutes: 20), () {}); + expect(async.nonPeriodicTimerCount, 2); + async.elapse(new Duration(minutes: 25)); + expect(async.nonPeriodicTimerCount, 1); + timer.cancel(); + expect(async.nonPeriodicTimerCount, 0); + }); + }); + }); + + group('timers', () { + test("should become inactive as soon as they're invoked", () { + return new FakeAsync().run((async) { + Timer timer; + timer = new Timer(elapseBy, expectAsync0(() { + expect(timer.isActive, isFalse); + })); + + expect(timer.isActive, isTrue); + async.elapse(elapseBy); + expect(timer.isActive, isFalse); }); }); }); From a4ba39eefa35dff82243abcdd4a96c88cfa9911e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Mar 2018 20:24:56 -0800 Subject: [PATCH 047/113] Make Duration.elapsed public This matches the old fake_async package API and cleans up test code considerably. --- pkgs/fake_async/lib/fake_async.dart | 1 + pkgs/fake_async/test/fake_async_test.dart | 28 ++++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 7ffc82ac9..5eb17348c 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -32,6 +32,7 @@ typedef void _Microtask(); class FakeAsync { /// The amount of fake time that's elapsed since this [FakeAsync] was /// created. + Duration get elapsed => _elapsed; var _elapsed = Duration.ZERO; /// The fake time at which the current call to [elapse] will finish running. diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 2268f2abd..71ffbf856 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -35,7 +35,7 @@ main() { test('should elapse time by the specified amount', () { var async = new FakeAsync(); async.elapseBlocking(elapseBy); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + expect(async.elapsed, elapseBy); }); test('should throw when called with a negative duration', () { @@ -48,7 +48,7 @@ main() { test('should elapse time by the specified amount', () { new FakeAsync().run((async) { async.elapse(elapseBy); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + expect(async.elapsed, elapseBy); }); }); @@ -80,25 +80,22 @@ main() { new Timer(elapseBy, () => async.elapseBlocking(elapseBy)); new Timer(elapseBy * 2, expectAsync0(() {})); async.elapse(elapseBy); - expect( - async.getClock(initialTime).now(), initialTime.add(elapseBy * 2)); + expect(async.elapsed, elapseBy * 2); }); }); test('should call timers at their scheduled time', () { new FakeAsync().run((async) { new Timer(elapseBy ~/ 2, expectAsync0(() { - expect(async.getClock(initialTime).now(), - initialTime.add(elapseBy ~/ 2)); + expect(async.elapsed, elapseBy ~/ 2); })); var periodicCalledAt = []; - new Timer.periodic(elapseBy ~/ 2, - (_) => periodicCalledAt.add(async.getClock(initialTime).now())); + new Timer.periodic( + elapseBy ~/ 2, (_) => periodicCalledAt.add(async.elapsed)); async.elapse(elapseBy); - expect( - periodicCalledAt, [elapseBy ~/ 2, elapseBy].map(initialTime.add)); + expect(periodicCalledAt, [elapseBy ~/ 2, elapseBy]); }); }); @@ -188,7 +185,7 @@ main() { test('should call microtasks before advancing time', () { new FakeAsync().run((async) { scheduleMicrotask(expectAsync0(() { - expect(async.getClock(initialTime).now(), initialTime); + expect(async.elapsed, Duration.ZERO); })); async.elapse(new Duration(minutes: 1)); }); @@ -198,7 +195,7 @@ main() { new FakeAsync().run((async) { var controller = new StreamController(); expect(controller.stream.first.then((_) { - expect(async.getClock(initialTime).now(), initialTime); + expect(async.elapsed, Duration.ZERO); }), completes); controller.add(null); async.elapse(new Duration(minutes: 1)); @@ -209,7 +206,7 @@ main() { new FakeAsync().run((async) { var negativeDuration = new Duration(days: -1); new Timer(negativeDuration, expectAsync0(() { - expect(async.getClock(initialTime).now(), initialTime); + expect(async.elapsed, Duration.ZERO); })); async.elapse(new Duration(minutes: 1)); }); @@ -219,8 +216,7 @@ main() { new FakeAsync().run((async) { new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); async.elapse(elapseBy); - expect( - async.getClock(initialTime).now(), initialTime.add(elapseBy * 5)); + expect(async.elapsed, elapseBy * 5); }); }); @@ -354,7 +350,7 @@ main() { async.flushTimers(timeout: elapseBy * 2); expect(log, [1, 2, 3]); - expect(async.getClock(initialTime).now(), initialTime.add(elapseBy)); + expect(async.elapsed, elapseBy); }); }); From 56720d04adc12e9e6af43872dd80213001e3c0a8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 5 Mar 2018 21:23:55 -0800 Subject: [PATCH 048/113] Fix a link in the README (dart-lang/fake_async#1) --- pkgs/fake_async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md index ea00b9b4c..70e46ba12 100644 --- a/pkgs/fake_async/README.md +++ b/pkgs/fake_async/README.md @@ -44,5 +44,5 @@ them to use the same notion of time as `dart:async` classes. [`new DateTime.now()`]: https://api.dartlang.org/stable/dart-core/DateTime/DateTime.now.html [`Stopwatch`]: https://api.dartlang.org/stable/dart-core/Stopwatch-class.html [`clock`]: https://pub.dartlang.org/packages/clock -[`clock.now`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html +[`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html [`clock.getStopwatch()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/getStopwatch.html From 265b08cfaf1662dbb7d7c92b54eef8e3a0569d89 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 6 Mar 2018 13:26:39 -0800 Subject: [PATCH 049/113] Code review --- pkgs/fake_async/lib/fake_async.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 5eb17348c..639632bfc 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -40,8 +40,7 @@ class FakeAsync { /// This is `null` if there's no current call to [elapse]. Duration _elapsingTo; - /// The queue of microtasks that are scheduled to run when fake time - /// progresses. + /// Tasks that are scheduled to run when fake time progresses. final _microtasks = new Queue<_Microtask>(); /// All timers created within [run]. From 112f56b2d6e028b57502e2b499baedfd162996a7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 6 Mar 2018 13:29:19 -0800 Subject: [PATCH 050/113] Fix a type error --- pkgs/fake_async/test/fake_async_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 71ffbf856..4c13da297 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -90,7 +90,7 @@ main() { expect(async.elapsed, elapseBy ~/ 2); })); - var periodicCalledAt = []; + var periodicCalledAt = []; new Timer.periodic( elapseBy ~/ 2, (_) => periodicCalledAt.add(async.elapsed)); From c0fd25c4bfb152a32b7d72c8c1d7f60654149270 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 7 Mar 2018 17:08:56 -0800 Subject: [PATCH 051/113] Set the default clock value within FakeAsync.run() (dart-lang/fake_async#3) This brings this package to full API compatibility with fake_async 0.1. --- pkgs/fake_async/CHANGELOG.md | 33 ++++++++++++- pkgs/fake_async/lib/fake_async.dart | 47 +++++++++++++++---- pkgs/fake_async/pubspec.yaml | 4 +- pkgs/fake_async/test/fake_async_test.dart | 57 +++++++++++++++++++++++ 4 files changed, 130 insertions(+), 11 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 5b26ad30f..513cab0e3 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,34 @@ ## 1.0.0 -* Initial version. +This release contains the `FakeAsync` class that was defined in [`quiver`][]. +It's backwards-compatible with both the `quiver` version *and* the old version +of the `fake_async` package. + +### New Features Relative to `quiver` + +* `FakeAsync.elapsed` returns the total amount of fake time elapsed since the + `FakeAsync` instance was created. + +* `new FakeAsync()` now takes an `initialTime` argument that sets the default + time for clocks created with `FakeAsync.getClock()`, and for the `clock` + package's top-level `clock` variable. + +### New Features Relative to `fake_async` 0.1 + +* `FakeAsync.periodicTimerCount`, `FakeAsync.nonPeriodicTimerCount`, and + `FakeAsync.microtaskCount` provide visibility into the events scheduled within + `FakeAsync.run()`. + +* `FakeAsync.getClock()` provides access to fully-featured `Clock` objects based + on `FakeAsync`'s elapsed time. + +* `FakeAsync.flushMicrotasks()` empties the microtask queue without elapsing any + time or running any timers. + +* `FakeAsync.flushTimers()` runs all microtasks and timers until there are no + more scheduled. + +## 0.1.2 + +* Integrate with the clock package. + diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 639632bfc..eb69efe50 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -15,8 +15,8 @@ import 'dart:async'; import 'dart:collection'; +import 'package:clock/clock.dart'; import 'package:collection/collection.dart'; -import 'package:quiver/time.dart'; /// The type of a microtask callback. typedef void _Microtask(); @@ -30,6 +30,9 @@ typedef void _Microtask(); /// The synchronous passage of time (as from blocking or expensive calls) can /// also be simulated using [elapseBlocking]. class FakeAsync { + /// The value of [clock] within [run]. + Clock _clock; + /// The amount of fake time that's elapsed since this [FakeAsync] was /// created. Duration get elapsed => _elapsed; @@ -57,12 +60,29 @@ class FakeAsync { /// The number of pending microtasks scheduled within a call to [run]. int get microtaskCount => _microtasks.length; + /// Creates a [FakeAsync]. + /// + /// Within [run], the [`clock`][] property will start at [initialTime] and + /// move forward as fake time elapses. + /// + /// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html + FakeAsync({DateTime initialTime}) { + initialTime ??= clock.now(); + _clock = new Clock(() => initialTime.add(elapsed)); + } + /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and /// [elapseBlocking]. /// /// The returned clock starts at [initialTime] plus the fake time that's /// already been elapsed. Further calls to [elapse] and [elapseBlocking] will /// advance the clock as well. + /// + /// Note that it's usually easier to use the top-level [`clock`][] property. + /// Only call this function if you want a different [initialTime] than the + /// default. + /// + /// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html Clock getClock(DateTime initialTime) => new Clock(() => initialTime.add(_elapsed)); @@ -110,15 +130,24 @@ class FakeAsync { /// asynchronous features used within [callback] are controlled by calls to /// [elapse] rather than the passing of real time. /// + /// The [`clock`][] property will be set to a clock that reports the fake + /// elapsed time. By default, it starts at the time the [FakeAsync] was + /// created (according to [`clock.now()`][]), but this can be controlled by + /// passing `initialTime` to [new FakeAsync]. + /// + /// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html + /// [`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html + /// /// Calls [callback] with `this` as argument and returns its result. - T run(T callback(FakeAsync self)) => runZoned(() => callback(this), - zoneSpecification: new ZoneSpecification( - createTimer: (_, __, ___, duration, callback) => - _createTimer(duration, callback, false), - createPeriodicTimer: (_, __, ___, duration, callback) => - _createTimer(duration, callback, true), - scheduleMicrotask: (_, __, ___, microtask) => - _microtasks.add(microtask))); + T run(T callback(FakeAsync self)) => + runZoned(() => withClock(_clock, () => callback(this)), + zoneSpecification: new ZoneSpecification( + createTimer: (_, __, ___, duration, callback) => + _createTimer(duration, callback, false), + createPeriodicTimer: (_, __, ___, duration, callback) => + _createTimer(duration, callback, true), + scheduleMicrotask: (_, __, ___, microtask) => + _microtasks.add(microtask))); /// Runs all pending microtasks scheduled within a call to [run] until there /// are no more microtasks scheduled. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 1d43a7b9a..af60de9e1 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -8,10 +8,12 @@ environment: sdk: '>=1.24.0 <2.0.0' dependencies: + clock: '^1.0.0' collection: '^1.8.0' - quiver: '^0.28.0' dev_dependencies: async: '>=1.10.0 <3.0.0' test: '^0.12.28' +dependency_overrides: + clock: {git: git://github.com/dart-lang/clock} diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 4c13da297..b96345c3e 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -14,6 +14,7 @@ import 'dart:async'; +import 'package:clock/clock.dart'; import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; @@ -506,4 +507,60 @@ main() { }); }); }); + + group('clock', () { + test('updates following elapse()', () { + new FakeAsync().run((async) { + var before = clock.now(); + async.elapse(elapseBy); + expect(clock.now(), before.add(elapseBy)); + }); + }); + + test('updates following elapseBlocking()', () { + new FakeAsync().run((async) { + var before = clock.now(); + async.elapseBlocking(elapseBy); + expect(clock.now(), before.add(elapseBy)); + }); + }); + + group('starts at', () { + test('the time at which the FakeAsync was created', () { + var start = new DateTime.now(); + new FakeAsync().run((async) { + expect(clock.now(), _closeToTime(start)); + async.elapse(elapseBy); + expect(clock.now(), _closeToTime(start.add(elapseBy))); + }); + }); + + test('the value of clock.now()', () { + var start = new DateTime(1990, 8, 11); + withClock(new Clock.fixed(start), () { + new FakeAsync().run((async) { + expect(clock.now(), start); + async.elapse(elapseBy); + expect(clock.now(), start.add(elapseBy)); + }); + }); + }); + + test('an explicit value', () { + var start = new DateTime(1990, 8, 11); + new FakeAsync(initialTime: start).run((async) { + expect(clock.now(), start); + async.elapse(elapseBy); + expect(clock.now(), start.add(elapseBy)); + }); + }); + }); + }); } + +/// Returns a matcher that asserts that a [DateTime] is within 100ms of +/// [expected]. +Matcher _closeToTime(DateTime expected) => predicate( + (actual) => + expected.difference(actual as DateTime).inMilliseconds.abs() < 100, + "is close to $expected"); From 4dd7aace756aac950396ffc234031353dfa6ab67 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 7 Mar 2018 17:23:49 -0800 Subject: [PATCH 052/113] Add a top-level fakeAsync() function (dart-lang/fake_async#4) --- pkgs/fake_async/CHANGELOG.md | 5 ++++ pkgs/fake_async/README.md | 8 +++--- pkgs/fake_async/lib/fake_async.dart | 44 +++++++++++++++++++++++------ pkgs/fake_async/pubspec.yaml | 2 +- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 513cab0e3..f1ed705aa 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -4,6 +4,11 @@ This release contains the `FakeAsync` class that was defined in [`quiver`][]. It's backwards-compatible with both the `quiver` version *and* the old version of the `fake_async` package. +### New Features + +* A top-level `fakeAsync()` function was added that encapsulates + `new FakeAsync().run(...)`. + ### New Features Relative to `quiver` * `FakeAsync.elapsed` returns the total amount of fake time elapsed since the diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md index 70e46ba12..9e42419b2 100644 --- a/pkgs/fake_async/README.md +++ b/pkgs/fake_async/README.md @@ -17,11 +17,11 @@ import 'package:test/test.dart'; void main() { test("Future.timeout() throws an error once the timeout is up", () { - // Any code run within [FakeAsync.run] is run within the context of that - // [FakeAsync] object. - new FakeAsync().run((async) { + // Any code run within [fakeAsync] is run within the context of the + // [FakeAsync] object passed to the callback. + fakeAsync((async) { // All asynchronous features that rely on timing are automatically - // controlled by [FakeAsync]. + // controlled by [fakeAsync]. expect(new Completer().future.timeout(new Duration(seconds: 5)), throwsA(new isInstanceOf())); diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index eb69efe50..707d179a2 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -21,6 +21,25 @@ import 'package:collection/collection.dart'; /// The type of a microtask callback. typedef void _Microtask(); +/// Runs [callback] in a [Zone] where all asynchrony is controlled by an +/// instance of [FakeAsync]. +/// +/// All [Future]s, [Stream]s, [Timer]s, microtasks, and other time-based +/// asynchronous features used within [callback] are controlled by calls to +/// [FakeAsync.elapse] rather than the passing of real time. +/// +/// The [`clock`][] property will be set to a clock that reports the fake +/// elapsed time. By default, it starts at the time [fakeAsync] was created +/// (according to [`clock.now()`][]), but this can be controlled by passing +/// [initialTime]. +/// +/// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html +/// [`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html +/// +/// Returns the result of [callback]. +T fakeAsync(T callback(FakeAsync async), {DateTime initialTime}) => + new FakeAsync(initialTime: initialTime).run(callback); + /// A class that mocks out the passage of time within a [Zone]. /// /// Test code can be passed as a callback to [run], which causes it to be run in @@ -49,15 +68,18 @@ class FakeAsync { /// All timers created within [run]. final _timers = new Set<_FakeTimer>(); - /// The number of active periodic timers created within a call to [run]. + /// The number of active periodic timers created within a call to [run] or + /// [fakeAsync]. int get periodicTimerCount => _timers.where((timer) => timer._isPeriodic).length; - /// The number of active non-periodic timers created within a call to [run]. + /// The number of active non-periodic timers created within a call to [run] or + /// [fakeAsync]. int get nonPeriodicTimerCount => _timers.where((timer) => !timer._isPeriodic).length; - /// The number of pending microtasks scheduled within a call to [run]. + /// The number of pending microtasks scheduled within a call to [run] or + /// [fakeAsync]. int get microtaskCount => _microtasks.length; /// Creates a [FakeAsync]. @@ -66,6 +88,9 @@ class FakeAsync { /// move forward as fake time elapses. /// /// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html + /// + /// Note: it's usually more convenient to use [fakeAsync] rather than creating + /// a [FakeAsync] object and calling [run] manually. FakeAsync({DateTime initialTime}) { initialTime ??= clock.now(); _clock = new Clock(() => initialTime.add(elapsed)); @@ -91,9 +116,9 @@ class FakeAsync { /// Throws an [ArgumentError] if [duration] is negative. Throws a [StateError] /// if a previous call to [elapse] has not yet completed. /// - /// Any timers created within [run] will fire if their time is within - /// [duration]. The microtask queue is processed before and after each timer - /// fires. + /// Any timers created within [run] or [fakeAsync] will fire if their time is + /// within [duration]. The microtask queue is processed before and after each + /// timer fires. void elapse(Duration duration) { if (duration.inMicroseconds < 0) { throw new ArgumentError.value( @@ -139,6 +164,9 @@ class FakeAsync { /// [`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html /// /// Calls [callback] with `this` as argument and returns its result. + /// + /// Note: it's usually more convenient to use [fakeAsync] rather than creating + /// a [FakeAsync] object and calling [run] manually. T run(T callback(FakeAsync self)) => runZoned(() => withClock(_clock, () => callback(this)), zoneSpecification: new ZoneSpecification( @@ -149,8 +177,8 @@ class FakeAsync { scheduleMicrotask: (_, __, ___, microtask) => _microtasks.add(microtask))); - /// Runs all pending microtasks scheduled within a call to [run] until there - /// are no more microtasks scheduled. + /// Runs all pending microtasks scheduled within a call to [run] or + /// [fakeAsync] until there are no more microtasks scheduled. /// /// Does not run timers. void flushMicrotasks() { diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index af60de9e1..d5285fbbd 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.0.0-dev +version: 1.0.0 description: Fake asynchronous events such as timers and microtasks for deterministic testing. author: Dart Team homepage: https://github.com/dart-lang/fake_async From 1bbbde886fc98eb293133ba69dbafb622231aa60 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 1 May 2018 15:56:54 -0700 Subject: [PATCH 053/113] Fix runtime Dart 2 failures in tests (dart-lang/fake_async#7) Works around dart-lang/sdkdart-lang/fake_async#33015 --- pkgs/fake_async/test/fake_async_test.dart | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index b96345c3e..f3a0e76c7 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -29,7 +29,7 @@ main() { group('elapseBlocking', () { test('should elapse time without calling timers', () { - new Timer(elapseBy ~/ 2, neverCalled); + new Timer(elapseBy ~/ 2, neverCalledVoid); new FakeAsync().elapseBlocking(elapseBy); }); @@ -102,14 +102,14 @@ main() { test('should not call timers expiring after end time', () { new FakeAsync().run((async) { - new Timer(elapseBy * 2, neverCalled); + new Timer(elapseBy * 2, neverCalledVoid); async.elapse(elapseBy); }); }); test('should not call canceled timers', () { new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, neverCalled); + var timer = new Timer(elapseBy ~/ 2, neverCalledVoid); timer.cancel(); async.elapse(elapseBy); }); @@ -564,3 +564,9 @@ Matcher _closeToTime(DateTime expected) => predicate( (actual) => expected.difference(actual as DateTime).inMilliseconds.abs() < 100, "is close to $expected"); + +/// A wrapper for [neverCalled] that works around sdk#33015. +void Function() get neverCalledVoid { + var function = neverCalled; + return () => neverCalled(); +} From d476f5d22d6694200a5d416916ff3433f3ca57c5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 1 May 2018 16:13:21 -0700 Subject: [PATCH 054/113] Fix a broken link in the changelog (dart-lang/fake_async#6) --- pkgs/fake_async/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index f1ed705aa..ac13fce95 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -4,6 +4,8 @@ This release contains the `FakeAsync` class that was defined in [`quiver`][]. It's backwards-compatible with both the `quiver` version *and* the old version of the `fake_async` package. +[`quiver`]: https://pub.dartlang.org/packages/quiver + ### New Features * A top-level `fakeAsync()` function was added that encapsulates From af13a771c8df2f7a571d770081f0dcc25261afa7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 1 May 2018 16:19:21 -0700 Subject: [PATCH 055/113] Remove the dependency override on clock (dart-lang/fake_async#8) --- pkgs/fake_async/pubspec.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index d5285fbbd..ba244336d 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -14,6 +14,3 @@ dependencies: dev_dependencies: async: '>=1.10.0 <3.0.0' test: '^0.12.28' - -dependency_overrides: - clock: {git: git://github.com/dart-lang/clock} From 3fd74b7e13158b7616d29fa50ac8584c335b4253 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Fri, 13 Jul 2018 13:41:46 -0700 Subject: [PATCH 056/113] Fix uppercase constants and deprecated matcher. --- pkgs/fake_async/CHANGELOG.md | 5 +++++ pkgs/fake_async/analysis_options.yaml | 2 -- pkgs/fake_async/lib/fake_async.dart | 4 ++-- pkgs/fake_async/pubspec.yaml | 4 ++-- pkgs/fake_async/test/fake_async_test.dart | 14 +++++++------- 5 files changed, 16 insertions(+), 13 deletions(-) delete mode 100644 pkgs/fake_async/analysis_options.yaml diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index ac13fce95..3323084d7 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.0.1 + +* Update to lowercase Dart core library constants. +* Fix use of deprecated `isInstanceOf` matcher. + ## 1.0.0 This release contains the `FakeAsync` class that was defined in [`quiver`][]. diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml deleted file mode 100644 index a10d4c5a0..000000000 --- a/pkgs/fake_async/analysis_options.yaml +++ /dev/null @@ -1,2 +0,0 @@ -analyzer: - strong-mode: true diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 707d179a2..36e5c964c 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -55,7 +55,7 @@ class FakeAsync { /// The amount of fake time that's elapsed since this [FakeAsync] was /// created. Duration get elapsed => _elapsed; - var _elapsed = Duration.ZERO; + var _elapsed = Duration.zero; /// The fake time at which the current call to [elapse] will finish running. /// @@ -280,7 +280,7 @@ class _FakeTimer implements Timer { } _FakeTimer(Duration duration, this._callback, this._isPeriodic, this._async) - : _duration = duration < Duration.ZERO ? Duration.ZERO : duration { + : _duration = duration < Duration.zero ? Duration.zero : duration { _nextCall = _async._elapsed + _duration; } diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index ba244336d..b4d1738c0 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,11 +1,11 @@ name: fake_async -version: 1.0.0 +version: 1.0.1 description: Fake asynchronous events such as timers and microtasks for deterministic testing. author: Dart Team homepage: https://github.com/dart-lang/fake_async environment: - sdk: '>=1.24.0 <2.0.0' + sdk: '>=2.0.0-dev.36.0 <3.0.0' dependencies: clock: '^1.0.0' diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index f3a0e76c7..9c3221213 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -186,7 +186,7 @@ main() { test('should call microtasks before advancing time', () { new FakeAsync().run((async) { scheduleMicrotask(expectAsync0(() { - expect(async.elapsed, Duration.ZERO); + expect(async.elapsed, Duration.zero); })); async.elapse(new Duration(minutes: 1)); }); @@ -196,7 +196,7 @@ main() { new FakeAsync().run((async) { var controller = new StreamController(); expect(controller.stream.first.then((_) { - expect(async.elapsed, Duration.ZERO); + expect(async.elapsed, Duration.zero); }), completes); controller.add(null); async.elapse(new Duration(minutes: 1)); @@ -207,7 +207,7 @@ main() { new FakeAsync().run((async) { var negativeDuration = new Duration(days: -1); new Timer(negativeDuration, expectAsync0(() { - expect(async.elapsed, Duration.ZERO); + expect(async.elapsed, Duration.zero); })); async.elapse(new Duration(minutes: 1)); }); @@ -215,7 +215,7 @@ main() { test('should not be additive with elapseBlocking', () { new FakeAsync().run((async) { - new Timer(Duration.ZERO, () => async.elapseBlocking(elapseBy * 5)); + new Timer(Duration.zero, () => async.elapseBlocking(elapseBy * 5)); async.elapse(elapseBy); expect(async.elapsed, elapseBy * 5); }); @@ -250,7 +250,7 @@ main() { test('should work with new Future()', () { new FakeAsync().run((async) { new Future(expectAsync0(() {})); - async.elapse(Duration.ZERO); + async.elapse(Duration.zero); }); }); @@ -265,7 +265,7 @@ main() { new FakeAsync().run((async) { var completer = new Completer(); expect(completer.future.timeout(elapseBy ~/ 2), - throwsA(new isInstanceOf())); + throwsA(new TypeMatcher())); async.elapse(elapseBy); completer.complete(); }); @@ -298,7 +298,7 @@ main() { async.elapse(new Duration(minutes: 1)); expect(errors, hasLength(1)); - expect(errors.first, new isInstanceOf()); + expect(errors.first, new TypeMatcher()); }); }); }); From 1ce309e06fc0a5525e77ed1f14c7199513fec6ae Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 19 Jul 2018 15:55:34 -0700 Subject: [PATCH 057/113] misc: fix Travis-CI config (dart-lang/fake_async#12) --- pkgs/fake_async/.travis.yml | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml index 9124e035b..fcca4df9a 100644 --- a/pkgs/fake_async/.travis.yml +++ b/pkgs/fake_async/.travis.yml @@ -1,28 +1,14 @@ language: dart -# By default, builds are run in containers. -# https://docs.travis-ci.com/user/getting-started/#Selecting-infrastructure-(optional) -# Set `sudo: true` to disable containers if you need to use sudo in your scripts. -# sudo: true - dart: -- dev -- stable + - dev # See https://docs.travis-ci.com/user/languages/dart/ for details. dart_task: -- test: --platform vm -# Uncomment this line to run tests on the browser. -# - test: --platform firefox - -# Only run one instance of the formatter and the analyzer, rather than running -# them against each Dart version. -matrix: - include: - - dart: stable - dart_task: dartfmt - - dart: dev - dart_task: dartanalyzer + - test: --platform vm + - test: --platform firefox + - dartfmt + - dartanalyzer # Only building master means that we don't run two builds for each pull request. branches: From 3c66013e6877edda141cb822d37b800ac4af3ce6 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 14 Aug 2018 12:08:49 -0700 Subject: [PATCH 058/113] Support the latest pkg:test (dart-lang/fake_async#13) --- pkgs/fake_async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index b4d1738c0..26c4a8945 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -13,4 +13,4 @@ dependencies: dev_dependencies: async: '>=1.10.0 <3.0.0' - test: '^0.12.28' + test: ^1.0.0 From e4caca4799734d35326d3fc3158ce88e5e8c54a8 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 12 Jun 2019 11:11:51 -0700 Subject: [PATCH 059/113] Test on oldest supported SDK, enable and fix many lints (dart-lang/fake_async#14) --- pkgs/fake_async/.travis.yml | 25 +- pkgs/fake_async/analysis_options.yaml | 100 ++++++++ pkgs/fake_async/lib/fake_async.dart | 46 ++-- pkgs/fake_async/pubspec.yaml | 10 +- pkgs/fake_async/test/fake_async_test.dart | 263 ++++++++++------------ 5 files changed, 270 insertions(+), 174 deletions(-) create mode 100644 pkgs/fake_async/analysis_options.yaml diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml index fcca4df9a..48e46ca18 100644 --- a/pkgs/fake_async/.travis.yml +++ b/pkgs/fake_async/.travis.yml @@ -1,19 +1,28 @@ language: dart dart: - - dev + - dev + - 2.0.0 -# See https://docs.travis-ci.com/user/languages/dart/ for details. dart_task: - - test: --platform vm - - test: --platform firefox - - dartfmt - - dartanalyzer + - test: --platform vm,chrome + +matrix: + include: + # Only validate formatting using the dev release + - dart: dev + dart_task: dartfmt + - dart: dev + dart_task: + dartanalyzer: --fatal-warnings --fatal-hints . + - dart: 2.0.0 + dart_task: + dartanalyzer: --fatal-warnings . # Only building master means that we don't run two builds for each pull request. branches: only: [master] cache: - directories: - - $HOME/.pub-cache + directories: + - $HOME/.pub-cache diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml new file mode 100644 index 000000000..923240550 --- /dev/null +++ b/pkgs/fake_async/analysis_options.yaml @@ -0,0 +1,100 @@ +include: package:pedantic/analysis_options.yaml +analyzer: + strong-mode: + implicit-casts: false + errors: + dead_code: error + override_on_non_overriding_method: error + unused_element: error + unused_import: error + unused_local_variable: error +linter: + rules: + - always_declare_return_types + - annotate_overrides + - avoid_bool_literals_in_conditional_expressions + - avoid_classes_with_only_static_members + - avoid_empty_else + - avoid_function_literals_in_foreach_calls + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_relative_lib_imports + - avoid_renaming_method_parameters + - avoid_return_types_on_setters + - avoid_returning_null + - avoid_returning_null_for_future + - avoid_returning_null_for_void + - avoid_returning_this + - avoid_shadowing_type_parameters + - avoid_single_cascade_in_expression_statements + - avoid_types_as_parameter_names + - avoid_unused_constructor_parameters + - await_only_futures + - camel_case_types + - cancel_subscriptions + - cascade_invocations + - comment_references + - constant_identifier_names + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - file_names + - hash_and_equals + - implementation_imports + - invariant_booleans + - iterable_contains_unrelated_type + - join_return_with_assignment + - library_names + - library_prefixes + - list_remove_unrelated_type + - literal_only_boolean_expressions + - no_adjacent_strings_in_list + - no_duplicate_case_values + - non_constant_identifier_names + - null_closures + - omit_local_variable_types + - only_throw_errors + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - prefer_adjacent_string_concatenation + # Wait for oldest supported SDK to be 2.2 + #- prefer_collection_literals + - prefer_conditional_assignment + #- prefer_const_constructors + - prefer_contains + - prefer_equal_for_default_values + - prefer_final_fields + #- prefer_final_locals + - prefer_generic_function_type_aliases + - prefer_initializing_formals + - prefer_interpolation_to_compose_strings + - prefer_is_empty + - prefer_is_not_empty + - prefer_null_aware_operators + - prefer_single_quotes + - prefer_typing_uninitialized_variables + - recursive_getters + - slash_for_doc_comments + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unawaited_futures + - unnecessary_await_in_return + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_getters_setters + - unnecessary_lambdas + - unnecessary_new + - unnecessary_null_aware_assignments + - unnecessary_parenthesis + - unnecessary_statements + - unnecessary_this + - unrelated_type_equality_checks + - use_function_type_syntax_for_parameters + - use_rethrow_when_possible + - valid_regexps + - void_checks diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 36e5c964c..d25be11b5 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -19,7 +19,7 @@ import 'package:clock/clock.dart'; import 'package:collection/collection.dart'; /// The type of a microtask callback. -typedef void _Microtask(); +typedef _Microtask = void Function(); /// Runs [callback] in a [Zone] where all asynchrony is controlled by an /// instance of [FakeAsync]. @@ -37,8 +37,8 @@ typedef void _Microtask(); /// [`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html /// /// Returns the result of [callback]. -T fakeAsync(T callback(FakeAsync async), {DateTime initialTime}) => - new FakeAsync(initialTime: initialTime).run(callback); +T fakeAsync(T Function(FakeAsync async) callback, {DateTime initialTime}) => + FakeAsync(initialTime: initialTime).run(callback); /// A class that mocks out the passage of time within a [Zone]. /// @@ -63,10 +63,10 @@ class FakeAsync { Duration _elapsingTo; /// Tasks that are scheduled to run when fake time progresses. - final _microtasks = new Queue<_Microtask>(); + final _microtasks = Queue<_Microtask>(); /// All timers created within [run]. - final _timers = new Set<_FakeTimer>(); + final _timers = Set<_FakeTimer>(); /// The number of active periodic timers created within a call to [run] or /// [fakeAsync]. @@ -93,7 +93,7 @@ class FakeAsync { /// a [FakeAsync] object and calling [run] manually. FakeAsync({DateTime initialTime}) { initialTime ??= clock.now(); - _clock = new Clock(() => initialTime.add(elapsed)); + _clock = Clock(() => initialTime.add(elapsed)); } /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and @@ -109,7 +109,7 @@ class FakeAsync { /// /// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html Clock getClock(DateTime initialTime) => - new Clock(() => initialTime.add(_elapsed)); + Clock(() => initialTime.add(_elapsed)); /// Simulates the asynchronous passage of time. /// @@ -121,10 +121,9 @@ class FakeAsync { /// timer fires. void elapse(Duration duration) { if (duration.inMicroseconds < 0) { - throw new ArgumentError.value( - duration, 'duration', 'may not be negative'); + throw ArgumentError.value(duration, 'duration', 'may not be negative'); } else if (_elapsingTo != null) { - throw new StateError('Cannot elapse until previous elapse is complete.'); + throw StateError('Cannot elapse until previous elapse is complete.'); } _elapsingTo = _elapsed + duration; @@ -142,14 +141,14 @@ class FakeAsync { /// Throws an [ArgumentError] if [duration] is negative. void elapseBlocking(Duration duration) { if (duration.inMicroseconds < 0) { - throw new ArgumentError('Cannot call elapse with negative duration'); + throw ArgumentError('Cannot call elapse with negative duration'); } _elapsed += duration; if (_elapsingTo != null && _elapsed > _elapsingTo) _elapsingTo = _elapsed; } - /// Runs [callback] in a [Zone] where all asynchrony is controlled by [this]. + /// Runs [callback] in a [Zone] where all asynchrony is controlled by `this`. /// /// All [Future]s, [Stream]s, [Timer]s, microtasks, and other time-based /// asynchronous features used within [callback] are controlled by calls to @@ -167,9 +166,9 @@ class FakeAsync { /// /// Note: it's usually more convenient to use [fakeAsync] rather than creating /// a [FakeAsync] object and calling [run] manually. - T run(T callback(FakeAsync self)) => + T run(T Function(FakeAsync self) callback) => runZoned(() => withClock(_clock, () => callback(this)), - zoneSpecification: new ZoneSpecification( + zoneSpecification: ZoneSpecification( createTimer: (_, __, ___, duration, callback) => _createTimer(duration, callback, false), createPeriodicTimer: (_, __, ___, duration, callback) => @@ -196,15 +195,14 @@ class FakeAsync { /// The [timeout] controls how much fake time may elapse before a [StateError] /// is thrown. This ensures that a periodic timer doesn't cause this method to /// deadlock. It defaults to one hour. - void flushTimers({Duration timeout, bool flushPeriodicTimers: true}) { + void flushTimers({Duration timeout, bool flushPeriodicTimers = true}) { timeout ??= const Duration(hours: 1); var absoluteTimeout = _elapsed + timeout; _fireTimersWhile((timer) { if (timer._nextCall > absoluteTimeout) { // TODO(nweiz): Make this a [TimeoutException]. - throw new StateError( - 'Exceeded timeout ${timeout} while flushing timers'); + throw StateError('Exceeded timeout $timeout while flushing timers'); } if (flushPeriodicTimers) return _timers.isNotEmpty; @@ -222,9 +220,9 @@ class FakeAsync { /// /// Microtasks are flushed before and after each timer is fired. Before each /// timer fires, [_elapsed] is updated to the appropriate duration. - void _fireTimersWhile(bool predicate(_FakeTimer timer)) { + void _fireTimersWhile(bool Function(_FakeTimer timer) predicate) { flushMicrotasks(); - while (true) { + for (;;) { if (_timers.isEmpty) break; var timer = minBy(_timers, (timer) => timer._nextCall); @@ -236,10 +234,10 @@ class FakeAsync { } } - /// Creates a new timer controlled by [this] that fires [callback] after + /// Creates a new timer controlled by `this` that fires [callback] after /// [duration] (or every [duration] if [periodic] is `true`). Timer _createTimer(Duration duration, Function callback, bool periodic) { - var timer = new _FakeTimer(duration, callback, periodic, this); + var timer = _FakeTimer(duration, callback, periodic, this); _timers.add(timer); return timer; } @@ -274,9 +272,9 @@ class _FakeTimer implements Timer { /// fired. Duration _nextCall; - // TODO: Dart 2.0 requires this method to be implemented. + @override int get tick { - throw new UnimplementedError("tick"); + throw UnimplementedError('tick'); } _FakeTimer(Duration duration, this._callback, this._isPeriodic, this._async) @@ -284,8 +282,10 @@ class _FakeTimer implements Timer { _nextCall = _async._elapsed + _duration; } + @override bool get isActive => _async._timers.contains(this); + @override void cancel() => _async._timers.remove(this); /// Fires this timer's callback and updates its state as necessary. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 26c4a8945..ef9dddb89 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,16 +1,16 @@ name: fake_async -version: 1.0.1 +version: 1.0.2-dev description: Fake asynchronous events such as timers and microtasks for deterministic testing. author: Dart Team homepage: https://github.com/dart-lang/fake_async environment: - sdk: '>=2.0.0-dev.36.0 <3.0.0' + sdk: '>=2.0.0 <3.0.0' dependencies: - clock: '^1.0.0' - collection: '^1.8.0' + clock: ^1.0.0 + collection: ^1.8.0 dev_dependencies: - async: '>=1.10.0 <3.0.0' + async: ^2.0.0 test: ^1.0.0 diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 9c3221213..4ad4ed4fd 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -16,51 +16,48 @@ import 'dart:async'; import 'package:clock/clock.dart'; import 'package:fake_async/fake_async.dart'; - import 'package:test/test.dart'; -main() { - var initialTime = new DateTime(2000); - var elapseBy = new Duration(days: 1); +void main() { + var initialTime = DateTime(2000); + var elapseBy = Duration(days: 1); test('should set initial time', () { - expect(new FakeAsync().getClock(initialTime).now(), initialTime); + expect(FakeAsync().getClock(initialTime).now(), initialTime); }); group('elapseBlocking', () { test('should elapse time without calling timers', () { - new Timer(elapseBy ~/ 2, neverCalledVoid); - new FakeAsync().elapseBlocking(elapseBy); + Timer(elapseBy ~/ 2, neverCalled); + FakeAsync().elapseBlocking(elapseBy); }); test('should elapse time by the specified amount', () { - var async = new FakeAsync(); - async.elapseBlocking(elapseBy); + var async = FakeAsync()..elapseBlocking(elapseBy); expect(async.elapsed, elapseBy); }); test('should throw when called with a negative duration', () { - expect(() => new FakeAsync().elapseBlocking(new Duration(days: -1)), + expect(() => FakeAsync().elapseBlocking(Duration(days: -1)), throwsArgumentError); }); }); group('elapse', () { test('should elapse time by the specified amount', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { async.elapse(elapseBy); expect(async.elapsed, elapseBy); }); }); test('should throw ArgumentError when called with a negative duration', () { - expect(() => new FakeAsync().elapse(new Duration(days: -1)), - throwsArgumentError); + expect(() => FakeAsync().elapse(Duration(days: -1)), throwsArgumentError); }); test('should throw when called before previous call is complete', () { - new FakeAsync().run((async) { - new Timer(elapseBy ~/ 2, expectAsync0(() { + FakeAsync().run((async) { + Timer(elapseBy ~/ 2, expectAsync0(() { expect(() => async.elapse(elapseBy), throwsStateError); })); async.elapse(elapseBy); @@ -69,30 +66,30 @@ main() { group('when creating timers', () { test('should call timers expiring before or at end time', () { - new FakeAsync().run((async) { - new Timer(elapseBy ~/ 2, expectAsync0(() {})); - new Timer(elapseBy, expectAsync0(() {})); + FakeAsync().run((async) { + Timer(elapseBy ~/ 2, expectAsync0(() {})); + Timer(elapseBy, expectAsync0(() {})); async.elapse(elapseBy); }); }); test('should call timers expiring due to elapseBlocking', () { - new FakeAsync().run((async) { - new Timer(elapseBy, () => async.elapseBlocking(elapseBy)); - new Timer(elapseBy * 2, expectAsync0(() {})); + FakeAsync().run((async) { + Timer(elapseBy, () => async.elapseBlocking(elapseBy)); + Timer(elapseBy * 2, expectAsync0(() {})); async.elapse(elapseBy); expect(async.elapsed, elapseBy * 2); }); }); test('should call timers at their scheduled time', () { - new FakeAsync().run((async) { - new Timer(elapseBy ~/ 2, expectAsync0(() { + FakeAsync().run((async) { + Timer(elapseBy ~/ 2, expectAsync0(() { expect(async.elapsed, elapseBy ~/ 2); })); var periodicCalledAt = []; - new Timer.periodic( + Timer.periodic( elapseBy ~/ 2, (_) => periodicCalledAt.add(async.elapsed)); async.elapse(elapseBy); @@ -101,44 +98,43 @@ main() { }); test('should not call timers expiring after end time', () { - new FakeAsync().run((async) { - new Timer(elapseBy * 2, neverCalledVoid); + FakeAsync().run((async) { + Timer(elapseBy * 2, neverCalled); async.elapse(elapseBy); }); }); test('should not call canceled timers', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, neverCalledVoid); - timer.cancel(); + FakeAsync().run((async) { + Timer(elapseBy ~/ 2, neverCalled).cancel(); async.elapse(elapseBy); }); }); test('should call periodic timers each time the duration elapses', () { - new FakeAsync().run((async) { - new Timer.periodic(elapseBy ~/ 10, expectAsync1((_) {}, count: 10)); + FakeAsync().run((async) { + Timer.periodic(elapseBy ~/ 10, expectAsync1((_) {}, count: 10)); async.elapse(elapseBy); }); }); test('should call timers occurring at the same time in FIFO order', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; - new Timer(elapseBy ~/ 2, () => log.add('1')); - new Timer(elapseBy ~/ 2, () => log.add('2')); + Timer(elapseBy ~/ 2, () => log.add('1')); + Timer(elapseBy ~/ 2, () => log.add('2')); async.elapse(elapseBy); expect(log, ['1', '2']); }); }); test('should maintain FIFO order even with periodic timers', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; - new Timer.periodic(elapseBy ~/ 2, (_) => log.add('periodic 1')); - new Timer(elapseBy ~/ 2, () => log.add('delayed 1')); - new Timer(elapseBy, () => log.add('delayed 2')); - new Timer.periodic(elapseBy, (_) => log.add('periodic 2')); + Timer.periodic(elapseBy ~/ 2, (_) => log.add('periodic 1')); + Timer(elapseBy ~/ 2, () => log.add('delayed 1')); + Timer(elapseBy, () => log.add('delayed 2')); + Timer.periodic(elapseBy, (_) => log.add('periodic 2')); async.elapse(elapseBy); expect(log, [ @@ -152,17 +148,17 @@ main() { }); test('should process microtasks surrounding each timer', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var microtaskCalls = 0; var timerCalls = 0; - scheduleMicrotasks() { + void scheduleMicrotasks() { for (var i = 0; i < 5; i++) { scheduleMicrotask(() => microtaskCalls++); } } scheduleMicrotasks(); - new Timer.periodic(elapseBy ~/ 5, (_) { + Timer.periodic(elapseBy ~/ 5, (_) { timerCalls++; expect(microtaskCalls, 5 * timerCalls); scheduleMicrotasks(); @@ -174,9 +170,9 @@ main() { }); test('should pass the periodic timer itself to callbacks', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { Timer constructed; - constructed = new Timer.periodic(elapseBy, expectAsync1((passed) { + constructed = Timer.periodic(elapseBy, expectAsync1((passed) { expect(passed, same(constructed)); })); async.elapse(elapseBy); @@ -184,38 +180,38 @@ main() { }); test('should call microtasks before advancing time', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { scheduleMicrotask(expectAsync0(() { expect(async.elapsed, Duration.zero); })); - async.elapse(new Duration(minutes: 1)); + async.elapse(Duration(minutes: 1)); }); }); test('should add event before advancing time', () { - new FakeAsync().run((async) { - var controller = new StreamController(); + FakeAsync().run((async) { + var controller = StreamController(); expect(controller.stream.first.then((_) { expect(async.elapsed, Duration.zero); }), completes); controller.add(null); - async.elapse(new Duration(minutes: 1)); + async.elapse(Duration(minutes: 1)); }); }); test('should increase negative duration timers to zero duration', () { - new FakeAsync().run((async) { - var negativeDuration = new Duration(days: -1); - new Timer(negativeDuration, expectAsync0(() { + FakeAsync().run((async) { + var negativeDuration = Duration(days: -1); + Timer(negativeDuration, expectAsync0(() { expect(async.elapsed, Duration.zero); })); - async.elapse(new Duration(minutes: 1)); + async.elapse(Duration(minutes: 1)); }); }); test('should not be additive with elapseBlocking', () { - new FakeAsync().run((async) { - new Timer(Duration.zero, () => async.elapseBlocking(elapseBy * 5)); + FakeAsync().run((async) { + Timer(Duration.zero, () => async.elapseBlocking(elapseBy * 5)); async.elapse(elapseBy); expect(async.elapsed, elapseBy * 5); }); @@ -223,49 +219,48 @@ main() { group('isActive', () { test('should be false after timer is run', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, () {}); + FakeAsync().run((async) { + var timer = Timer(elapseBy ~/ 2, () {}); async.elapse(elapseBy); expect(timer.isActive, isFalse); }); }); test('should be true after periodic timer is run', () { - new FakeAsync().run((async) { - var timer = new Timer.periodic(elapseBy ~/ 2, (_) {}); + FakeAsync().run((async) { + var timer = Timer.periodic(elapseBy ~/ 2, (_) {}); async.elapse(elapseBy); expect(timer.isActive, isTrue); }); }); test('should be false after timer is canceled', () { - new FakeAsync().run((async) { - var timer = new Timer(elapseBy ~/ 2, () {}); - timer.cancel(); + FakeAsync().run((async) { + var timer = Timer(elapseBy ~/ 2, () {})..cancel(); expect(timer.isActive, isFalse); }); }); }); test('should work with new Future()', () { - new FakeAsync().run((async) { - new Future(expectAsync0(() {})); + FakeAsync().run((async) { + Future(expectAsync0(() {})); async.elapse(Duration.zero); }); }); test('should work with Future.delayed', () { - new FakeAsync().run((async) { - new Future.delayed(elapseBy, expectAsync0(() {})); + FakeAsync().run((async) { + Future.delayed(elapseBy, expectAsync0(() {})); async.elapse(elapseBy); }); }); test('should work with Future.timeout', () { - new FakeAsync().run((async) { - var completer = new Completer(); + FakeAsync().run((async) { + var completer = Completer(); expect(completer.future.timeout(elapseBy ~/ 2), - throwsA(new TypeMatcher())); + throwsA(TypeMatcher())); async.elapse(elapseBy); completer.complete(); }); @@ -276,29 +271,29 @@ main() { // // See https://code.google.com/p/dart/issues/detail?id=18149 test('should work with Stream.periodic', () { - new FakeAsync().run((async) { - expect(new Stream.periodic(new Duration(minutes: 1), (i) => i), + FakeAsync().run((async) { + expect(Stream.periodic(Duration(minutes: 1), (i) => i), emitsInOrder([0, 1, 2])); - async.elapse(new Duration(minutes: 3)); + async.elapse(Duration(minutes: 3)); }); }); test('should work with Stream.timeout', () { - new FakeAsync().run((async) { - var controller = new StreamController(); - var timed = controller.stream.timeout(new Duration(minutes: 2)); + FakeAsync().run((async) { + var controller = StreamController(); + var timed = controller.stream.timeout(Duration(minutes: 2)); var events = []; var errors = []; timed.listen(events.add, onError: errors.add); controller.add(0); - async.elapse(new Duration(minutes: 1)); + async.elapse(Duration(minutes: 1)); expect(events, [0]); - async.elapse(new Duration(minutes: 1)); + async.elapse(Duration(minutes: 1)); expect(errors, hasLength(1)); - expect(errors.first, new TypeMatcher()); + expect(errors.first, TypeMatcher()); }); }); }); @@ -306,14 +301,14 @@ main() { group('flushMicrotasks', () { test('should flush a microtask', () { - new FakeAsync().run((async) { - new Future.microtask(expectAsync0(() {})); + FakeAsync().run((async) { + Future.microtask(expectAsync0(() {})); async.flushMicrotasks(); }); }); test('should flush microtasks scheduled by microtasks in order', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; scheduleMicrotask(() { log.add(1); @@ -327,11 +322,11 @@ main() { }); test('should not run timers', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; scheduleMicrotask(() => log.add(1)); Timer.run(() => log.add(2)); - new Timer.periodic(new Duration(seconds: 1), (_) => log.add(2)); + Timer.periodic(Duration(seconds: 1), (_) => log.add(2)); async.flushMicrotasks(); expect(log, [1]); @@ -341,11 +336,11 @@ main() { group('flushTimers', () { test('should flush timers in FIFO order', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; Timer.run(() { log.add(1); - new Timer(elapseBy, () => log.add(3)); + Timer(elapseBy, () => log.add(3)); }); Timer.run(() => log.add(2)); @@ -358,11 +353,10 @@ main() { test( 'should run collateral periodic timers with non-periodic first if ' 'scheduled first', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; - new Timer(new Duration(seconds: 2), () => log.add('delayed')); - new Timer.periodic( - new Duration(seconds: 1), (_) => log.add('periodic')); + Timer(Duration(seconds: 2), () => log.add('delayed')); + Timer.periodic(Duration(seconds: 1), (_) => log.add('periodic')); async.flushTimers(flushPeriodicTimers: false); expect(log, ['periodic', 'delayed', 'periodic']); @@ -372,11 +366,10 @@ main() { test( 'should run collateral periodic timers with periodic first ' 'if scheduled first', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var log = []; - new Timer.periodic( - new Duration(seconds: 1), (_) => log.add('periodic')); - new Timer(new Duration(seconds: 2), () => log.add('delayed')); + Timer.periodic(Duration(seconds: 1), (_) => log.add('periodic')); + Timer(Duration(seconds: 2), () => log.add('delayed')); async.flushTimers(flushPeriodicTimers: false); expect(log, ['periodic', 'periodic', 'delayed']); @@ -384,10 +377,10 @@ main() { }); test('should time out', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { // Schedule 3 timers. All but the last one should fire. for (var delay in [30, 60, 90]) { - new Timer(new Duration(minutes: delay), + Timer(Duration(minutes: delay), expectAsync0(() {}, count: delay == 90 ? 0 : 1)); } @@ -396,56 +389,56 @@ main() { }); test('should time out a chain of timers', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var count = 0; - createTimer() { - new Timer(new Duration(minutes: 30), () { + void createTimer() { + Timer(Duration(minutes: 30), () { count++; createTimer(); }); } createTimer(); - expect(() => async.flushTimers(timeout: new Duration(hours: 2)), + expect(() => async.flushTimers(timeout: Duration(hours: 2)), throwsStateError); expect(count, 4); }); }); test('should time out periodic timers', () { - new FakeAsync().run((async) { - new Timer.periodic( - new Duration(minutes: 30), expectAsync1((_) {}, count: 2)); - expect(() => async.flushTimers(timeout: new Duration(hours: 1)), + FakeAsync().run((async) { + Timer.periodic(Duration(minutes: 30), expectAsync1((_) {}, count: 2)); + expect(() => async.flushTimers(timeout: Duration(hours: 1)), throwsStateError); }); }); test('should flush periodic timers', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var count = 0; - new Timer.periodic(new Duration(minutes: 30), (timer) { + Timer.periodic(Duration(minutes: 30), (timer) { if (count == 3) timer.cancel(); count++; }); - async.flushTimers(timeout: new Duration(hours: 20)); + async.flushTimers(timeout: Duration(hours: 20)); expect(count, 4); }); }); test('should compute absolute timeout as elapsed + timeout', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var count = 0; - createTimer() { - new Timer(new Duration(minutes: 30), () { + void createTimer() { + Timer(Duration(minutes: 30), () { count++; if (count < 4) createTimer(); }); } createTimer(); - async.elapse(new Duration(hours: 1)); - async.flushTimers(timeout: new Duration(hours: 1)); + async + ..elapse(Duration(hours: 1)) + ..flushTimers(timeout: Duration(hours: 1)); expect(count, 4); }); }); @@ -453,7 +446,7 @@ main() { group('stats', () { test('should report the number of pending microtasks', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { expect(async.microtaskCount, 0); scheduleMicrotask(() => null); expect(async.microtaskCount, 1); @@ -465,13 +458,13 @@ main() { }); test('it should report the number of pending periodic timers', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { expect(async.periodicTimerCount, 0); - var timer = new Timer.periodic(new Duration(minutes: 30), (_) {}); + var timer = Timer.periodic(Duration(minutes: 30), (_) {}); expect(async.periodicTimerCount, 1); - new Timer.periodic(new Duration(minutes: 20), (_) {}); + Timer.periodic(Duration(minutes: 20), (_) {}); expect(async.periodicTimerCount, 2); - async.elapse(new Duration(minutes: 20)); + async.elapse(Duration(minutes: 20)); expect(async.periodicTimerCount, 2); timer.cancel(); expect(async.periodicTimerCount, 1); @@ -479,13 +472,13 @@ main() { }); test('it should report the number of pending non periodic timers', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { expect(async.nonPeriodicTimerCount, 0); - Timer timer = new Timer(new Duration(minutes: 30), () {}); + var timer = Timer(Duration(minutes: 30), () {}); expect(async.nonPeriodicTimerCount, 1); - new Timer(new Duration(minutes: 20), () {}); + Timer(Duration(minutes: 20), () {}); expect(async.nonPeriodicTimerCount, 2); - async.elapse(new Duration(minutes: 25)); + async.elapse(Duration(minutes: 25)); expect(async.nonPeriodicTimerCount, 1); timer.cancel(); expect(async.nonPeriodicTimerCount, 0); @@ -495,9 +488,9 @@ main() { group('timers', () { test("should become inactive as soon as they're invoked", () { - return new FakeAsync().run((async) { + return FakeAsync().run((async) { Timer timer; - timer = new Timer(elapseBy, expectAsync0(() { + timer = Timer(elapseBy, expectAsync0(() { expect(timer.isActive, isFalse); })); @@ -510,7 +503,7 @@ main() { group('clock', () { test('updates following elapse()', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var before = clock.now(); async.elapse(elapseBy); expect(clock.now(), before.add(elapseBy)); @@ -518,7 +511,7 @@ main() { }); test('updates following elapseBlocking()', () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var before = clock.now(); async.elapseBlocking(elapseBy); expect(clock.now(), before.add(elapseBy)); @@ -527,8 +520,8 @@ main() { group('starts at', () { test('the time at which the FakeAsync was created', () { - var start = new DateTime.now(); - new FakeAsync().run((async) { + var start = DateTime.now(); + FakeAsync().run((async) { expect(clock.now(), _closeToTime(start)); async.elapse(elapseBy); expect(clock.now(), _closeToTime(start.add(elapseBy))); @@ -536,9 +529,9 @@ main() { }); test('the value of clock.now()', () { - var start = new DateTime(1990, 8, 11); - withClock(new Clock.fixed(start), () { - new FakeAsync().run((async) { + var start = DateTime(1990, 8, 11); + withClock(Clock.fixed(start), () { + FakeAsync().run((async) { expect(clock.now(), start); async.elapse(elapseBy); expect(clock.now(), start.add(elapseBy)); @@ -547,8 +540,8 @@ main() { }); test('an explicit value', () { - var start = new DateTime(1990, 8, 11); - new FakeAsync(initialTime: start).run((async) { + var start = DateTime(1990, 8, 11); + FakeAsync(initialTime: start).run((async) { expect(clock.now(), start); async.elapse(elapseBy); expect(clock.now(), start.add(elapseBy)); @@ -563,10 +556,4 @@ main() { Matcher _closeToTime(DateTime expected) => predicate( (actual) => expected.difference(actual as DateTime).inMilliseconds.abs() < 100, - "is close to $expected"); - -/// A wrapper for [neverCalled] that works around sdk#33015. -void Function() get neverCalledVoid { - var function = neverCalled; - return () => neverCalled(); -} + 'is close to $expected'); From 754f8fcf11f22bb6f2cab63c40bf16351b3bc2bb Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 18 Dec 2019 14:32:19 -0800 Subject: [PATCH 060/113] Fix latest lints, bump SDK constraint (dart-lang/fake_async#15) - prefer_collection_literals Bump minimum SDK to 2.2.0 to allow for Set literals. --- pkgs/fake_async/.travis.yml | 4 ++-- pkgs/fake_async/CHANGELOG.md | 4 ++++ pkgs/fake_async/analysis_options.yaml | 1 - pkgs/fake_async/lib/fake_async.dart | 2 +- pkgs/fake_async/pubspec.yaml | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml index 48e46ca18..581bd4b00 100644 --- a/pkgs/fake_async/.travis.yml +++ b/pkgs/fake_async/.travis.yml @@ -2,7 +2,7 @@ language: dart dart: - dev - - 2.0.0 + - 2.2.0 dart_task: - test: --platform vm,chrome @@ -15,7 +15,7 @@ matrix: - dart: dev dart_task: dartanalyzer: --fatal-warnings --fatal-hints . - - dart: 2.0.0 + - dart: 2.2.0 dart_task: dartanalyzer: --fatal-warnings . diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 3323084d7..9a71861fc 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Update min SDK to 2.2.0 + ## 1.0.1 * Update to lowercase Dart core library constants. diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index 923240550..2bf511a21 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -4,7 +4,6 @@ analyzer: implicit-casts: false errors: dead_code: error - override_on_non_overriding_method: error unused_element: error unused_import: error unused_local_variable: error diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index d25be11b5..5cdb7cdde 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -66,7 +66,7 @@ class FakeAsync { final _microtasks = Queue<_Microtask>(); /// All timers created within [run]. - final _timers = Set<_FakeTimer>(); + final _timers = <_FakeTimer>{}; /// The number of active periodic timers created within a call to [run] or /// [fakeAsync]. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index ef9dddb89..5582736c1 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -5,7 +5,7 @@ author: Dart Team homepage: https://github.com/dart-lang/fake_async environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.2.0 <3.0.0' dependencies: clock: ^1.0.0 From c99ddbe124e8fada9ec390b0b57108334cfd6810 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Mon, 6 Apr 2020 11:14:25 -0700 Subject: [PATCH 061/113] Expose FakeTimer, add FakeAsync.pendingTimers (dart-lang/fake_async#17) - Exposed the `FakeTimer` class as a public class. - Added `debugString` getter as well as making duration, creationStackTrace, and isPeriodic public. - Added `FakeAsync.pendingTimers` which gives access to all pending timers at the time of the call. --- pkgs/fake_async/CHANGELOG.md | 6 ++++ pkgs/fake_async/lib/fake_async.dart | 44 +++++++++++++++-------- pkgs/fake_async/pubspec.yaml | 2 +- pkgs/fake_async/test/fake_async_test.dart | 27 ++++++++++++++ 4 files changed, 64 insertions(+), 15 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 9a71861fc..a5e69fb2b 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.1.0 + + * Exposed the `FakeTimer` class as a public class. + * Added `FakeAsync.pendingTimers` which gives access to all pending timers + at the time of the call. + ## 1.0.2 * Update min SDK to 2.2.0 diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 5cdb7cdde..5a79ffb2b 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -66,17 +66,24 @@ class FakeAsync { final _microtasks = Queue<_Microtask>(); /// All timers created within [run]. - final _timers = <_FakeTimer>{}; + final _timers = {}; + + /// All the current pending timers. + List get pendingTimers => _timers.toList(growable: false); + + /// The debug strings for all the current pending timers. + List get pendingTimersDebugString => + pendingTimers.map((timer) => timer.debugString).toList(growable: false); /// The number of active periodic timers created within a call to [run] or /// [fakeAsync]. int get periodicTimerCount => - _timers.where((timer) => timer._isPeriodic).length; + _timers.where((timer) => timer.isPeriodic).length; /// The number of active non-periodic timers created within a call to [run] or /// [fakeAsync]. int get nonPeriodicTimerCount => - _timers.where((timer) => !timer._isPeriodic).length; + _timers.where((timer) => !timer.isPeriodic).length; /// The number of pending microtasks scheduled within a call to [run] or /// [fakeAsync]. @@ -211,7 +218,7 @@ class FakeAsync { // every periodic timer has had a change to run against the final // value of [_elapsed]. return _timers - .any((timer) => !timer._isPeriodic || timer._nextCall <= _elapsed); + .any((timer) => !timer.isPeriodic || timer._nextCall <= _elapsed); }); } @@ -220,7 +227,7 @@ class FakeAsync { /// /// Microtasks are flushed before and after each timer is fired. Before each /// timer fires, [_elapsed] is updated to the appropriate duration. - void _fireTimersWhile(bool Function(_FakeTimer timer) predicate) { + void _fireTimersWhile(bool Function(FakeTimer timer) predicate) { flushMicrotasks(); for (;;) { if (_timers.isEmpty) break; @@ -237,7 +244,7 @@ class FakeAsync { /// Creates a new timer controlled by `this` that fires [callback] after /// [duration] (or every [duration] if [periodic] is `true`). Timer _createTimer(Duration duration, Function callback, bool periodic) { - var timer = _FakeTimer(duration, callback, periodic, this); + var timer = FakeTimer._(duration, callback, periodic, this); _timers.add(timer); return timer; } @@ -249,12 +256,12 @@ class FakeAsync { } /// An implementation of [Timer] that's controlled by a [FakeAsync]. -class _FakeTimer implements Timer { +class FakeTimer implements Timer { /// If this is periodic, the time that should elapse between firings of this /// timer. /// /// This is not used by non-periodic timers. - final Duration _duration; + final Duration duration; /// The callback to invoke when the timer fires. /// @@ -263,7 +270,7 @@ class _FakeTimer implements Timer { final Function _callback; /// Whether this is a periodic timer. - final bool _isPeriodic; + final bool isPeriodic; /// The [FakeAsync] instance that controls this timer. final FakeAsync _async; @@ -272,14 +279,23 @@ class _FakeTimer implements Timer { /// fired. Duration _nextCall; + /// The current stack trace when this timer was created. + final creationStackTrace = StackTrace.current; + @override int get tick { throw UnimplementedError('tick'); } - _FakeTimer(Duration duration, this._callback, this._isPeriodic, this._async) - : _duration = duration < Duration.zero ? Duration.zero : duration { - _nextCall = _async._elapsed + _duration; + /// Returns debugging information to try to identify the source of the + /// [Timer]. + String get debugString => + 'Timer (duration: $duration, periodic: $isPeriodic), created:\n' + '$creationStackTrace'; + + FakeTimer._(Duration duration, this._callback, this.isPeriodic, this._async) + : duration = duration < Duration.zero ? Duration.zero : duration { + _nextCall = _async._elapsed + this.duration; } @override @@ -291,9 +307,9 @@ class _FakeTimer implements Timer { /// Fires this timer's callback and updates its state as necessary. void _fire() { assert(isActive); - if (_isPeriodic) { + if (isPeriodic) { _callback(this); - _nextCall += _duration; + _nextCall += duration; } else { cancel(); _callback(); diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 5582736c1..73b277411 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.0.2-dev +version: 1.1.0 description: Fake asynchronous events such as timers and microtasks for deterministic testing. author: Dart Team homepage: https://github.com/dart-lang/fake_async diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 4ad4ed4fd..d6095332a 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -484,6 +484,33 @@ void main() { expect(async.nonPeriodicTimerCount, 0); }); }); + + test('should report debugging information of pending timers', () { + FakeAsync().run((fakeAsync) { + expect(fakeAsync.pendingTimers, isEmpty); + var nonPeriodic = Timer(const Duration(seconds: 1), () {}) as FakeTimer; + var periodic = + Timer.periodic(const Duration(seconds: 2), (Timer timer) {}) + as FakeTimer; + final debugInfo = fakeAsync.pendingTimers; + expect(debugInfo.length, 2); + expect( + debugInfo, + containsAll([ + nonPeriodic, + periodic, + ]), + ); + + const thisFileName = 'fake_async_test.dart'; + expect(nonPeriodic.debugString, contains(':01.0')); + expect(nonPeriodic.debugString, contains('periodic: false')); + expect(nonPeriodic.debugString, contains(thisFileName)); + expect(periodic.debugString, contains(':02.0')); + expect(periodic.debugString, contains('periodic: true')); + expect(periodic.debugString, contains(thisFileName)); + }); + }); }); group('timers', () { From c5e1c032694554e4c0e0fae655c1e3399db78555 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 8 Jun 2020 16:52:39 -0700 Subject: [PATCH 062/113] Fix deprecated URLs (dart-lang/fake_async#21) --- pkgs/fake_async/CHANGELOG.md | 2 +- pkgs/fake_async/README.md | 14 +++++++------- pkgs/fake_async/pubspec.yaml | 7 ++++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index a5e69fb2b..54b4366b7 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -19,7 +19,7 @@ This release contains the `FakeAsync` class that was defined in [`quiver`][]. It's backwards-compatible with both the `quiver` version *and* the old version of the `fake_async` package. -[`quiver`]: https://pub.dartlang.org/packages/quiver +[`quiver`]: https://pub.dev/packages/quiver ### New Features diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md index 9e42419b2..1c1da30ec 100644 --- a/pkgs/fake_async/README.md +++ b/pkgs/fake_async/README.md @@ -35,14 +35,14 @@ void main() { ## Integration With `clock` -`FakeAsync` can't control the time reported by [`new DateTime.now()`][] or by +`FakeAsync` can't control the time reported by [`DateTime.now()`][] or by the [`Stopwatch`][] class, since they're not part of `dart:async`. However, if you create them using the [`clock`][] package's [`clock.now()`][] or -[`clock.getStopwatch()`][] functions, `FakeAsync` will automatically override +[`clock.stopwatch()`][] functions, `FakeAsync` will automatically override them to use the same notion of time as `dart:async` classes. -[`new DateTime.now()`]: https://api.dartlang.org/stable/dart-core/DateTime/DateTime.now.html -[`Stopwatch`]: https://api.dartlang.org/stable/dart-core/Stopwatch-class.html -[`clock`]: https://pub.dartlang.org/packages/clock -[`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html -[`clock.getStopwatch()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/getStopwatch.html +[`DateTime.now()`]: https://api.dart.dev/stable/dart-core/DateTime/DateTime.now.html +[`Stopwatch`]: https://api.dart.dev/stable/dart-core/Stopwatch-class.html +[`clock`]: https://pub.dev/packages/clock +[`clock.now()`]: https://pub.dev/documentation/clock/latest/clock/Clock/now.html +[`clock.stopwatch()`]: https://pub.dev/documentation/clock/latest/clock/Clock/stopwatch.html diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 73b277411..ef71b2650 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,7 +1,8 @@ name: fake_async -version: 1.1.0 -description: Fake asynchronous events such as timers and microtasks for deterministic testing. -author: Dart Team +version: 1.1.1-dev +description: >- + Fake asynchronous events such as timers and microtasks for deterministic + testing. homepage: https://github.com/dart-lang/fake_async environment: From 0014dce9a43e1665cae6cb6344ff6a0f5df38ed6 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Wed, 22 Jul 2020 12:30:21 -0700 Subject: [PATCH 063/113] Merge the null safety branch into master (dart-lang/fake_async#22) --- pkgs/fake_async/.travis.yml | 17 +----- pkgs/fake_async/CHANGELOG.md | 4 ++ pkgs/fake_async/analysis_options.yaml | 2 + pkgs/fake_async/lib/fake_async.dart | 29 +++++----- pkgs/fake_async/pubspec.yaml | 68 +++++++++++++++++++++-- pkgs/fake_async/test/fake_async_test.dart | 8 +-- 6 files changed, 92 insertions(+), 36 deletions(-) diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml index 581bd4b00..187244c87 100644 --- a/pkgs/fake_async/.travis.yml +++ b/pkgs/fake_async/.travis.yml @@ -1,23 +1,12 @@ language: dart dart: - - dev - - 2.2.0 + - preview/raw/2.10.0-0.2-dev dart_task: - test: --platform vm,chrome - -matrix: - include: - # Only validate formatting using the dev release - - dart: dev - dart_task: dartfmt - - dart: dev - dart_task: - dartanalyzer: --fatal-warnings --fatal-hints . - - dart: 2.2.0 - dart_task: - dartanalyzer: --fatal-warnings . + - dartfmt + - dartanalyzer: --fatal-warnings --fatal-hints . # Only building master means that we don't run two builds for each pull request. branches: diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 54b4366b7..93b7a4b04 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.1.0-nullsafety + +* Update to null safety. + ## 1.1.0 * Exposed the `FakeTimer` class as a public class. diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index 2bf511a21..55139b7a1 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -2,6 +2,8 @@ include: package:pedantic/analysis_options.yaml analyzer: strong-mode: implicit-casts: false + enable-experiment: + - non-nullable errors: dead_code: error unused_element: error diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 5a79ffb2b..52dd3ba03 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -37,7 +37,7 @@ typedef _Microtask = void Function(); /// [`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html /// /// Returns the result of [callback]. -T fakeAsync(T Function(FakeAsync async) callback, {DateTime initialTime}) => +T fakeAsync(T Function(FakeAsync async) callback, {DateTime? initialTime}) => FakeAsync(initialTime: initialTime).run(callback); /// A class that mocks out the passage of time within a [Zone]. @@ -50,7 +50,7 @@ T fakeAsync(T Function(FakeAsync async) callback, {DateTime initialTime}) => /// also be simulated using [elapseBlocking]. class FakeAsync { /// The value of [clock] within [run]. - Clock _clock; + late final Clock _clock; /// The amount of fake time that's elapsed since this [FakeAsync] was /// created. @@ -60,7 +60,7 @@ class FakeAsync { /// The fake time at which the current call to [elapse] will finish running. /// /// This is `null` if there's no current call to [elapse]. - Duration _elapsingTo; + Duration? _elapsingTo; /// Tasks that are scheduled to run when fake time progresses. final _microtasks = Queue<_Microtask>(); @@ -98,9 +98,9 @@ class FakeAsync { /// /// Note: it's usually more convenient to use [fakeAsync] rather than creating /// a [FakeAsync] object and calling [run] manually. - FakeAsync({DateTime initialTime}) { - initialTime ??= clock.now(); - _clock = Clock(() => initialTime.add(elapsed)); + FakeAsync({DateTime? initialTime}) { + var nonNullInitialTime = initialTime ?? clock.now(); + _clock = Clock(() => nonNullInitialTime.add(elapsed)); } /// Returns a fake [Clock] whose time can is elapsed by calls to [elapse] and @@ -134,8 +134,8 @@ class FakeAsync { } _elapsingTo = _elapsed + duration; - _fireTimersWhile((next) => next._nextCall <= _elapsingTo); - _elapseTo(_elapsingTo); + _fireTimersWhile((next) => next._nextCall <= _elapsingTo!); + _elapseTo(_elapsingTo!); _elapsingTo = null; } @@ -152,7 +152,8 @@ class FakeAsync { } _elapsed += duration; - if (_elapsingTo != null && _elapsed > _elapsingTo) _elapsingTo = _elapsed; + var elapsingTo = _elapsingTo; + if (elapsingTo != null && _elapsed > elapsingTo) _elapsingTo = _elapsed; } /// Runs [callback] in a [Zone] where all asynchrony is controlled by `this`. @@ -202,9 +203,9 @@ class FakeAsync { /// The [timeout] controls how much fake time may elapse before a [StateError] /// is thrown. This ensures that a periodic timer doesn't cause this method to /// deadlock. It defaults to one hour. - void flushTimers({Duration timeout, bool flushPeriodicTimers = true}) { - timeout ??= const Duration(hours: 1); - + void flushTimers( + {Duration timeout = const Duration(hours: 1), + bool flushPeriodicTimers = true}) { var absoluteTimeout = _elapsed + timeout; _fireTimersWhile((timer) { if (timer._nextCall > absoluteTimeout) { @@ -232,7 +233,7 @@ class FakeAsync { for (;;) { if (_timers.isEmpty) break; - var timer = minBy(_timers, (timer) => timer._nextCall); + var timer = minBy(_timers, (FakeTimer timer) => timer._nextCall)!; if (!predicate(timer)) break; _elapseTo(timer._nextCall); @@ -277,7 +278,7 @@ class FakeTimer implements Timer { /// The value of [FakeAsync._elapsed] at (or after) which this timer should be /// fired. - Duration _nextCall; + late Duration _nextCall; /// The current stack trace when this timer was created. final creationStackTrace = StackTrace.current; diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index ef71b2650..7e05dfd4c 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,17 +1,77 @@ name: fake_async -version: 1.1.1-dev +version: 1.1.0-nullsafety description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. homepage: https://github.com/dart-lang/fake_async environment: - sdk: '>=2.2.0 <3.0.0' + # This must remain a tight constraint until nnbd is stable + sdk: '>=2.10.0-0 <2.10.0' dependencies: - clock: ^1.0.0 - collection: ^1.8.0 + clock: '>=1.1.0-nullsafety <1.1.0' + collection: '>=1.15.0-nullsafety <1.15.0' dev_dependencies: async: ^2.0.0 test: ^1.0.0 + + +dependency_overrides: + async: + git: git://github.com/dart-lang/async.git + boolean_selector: + git: git://github.com/dart-lang/boolean_selector.git + charcode: + git: git://github.com/dart-lang/charcode.git + clock: + git: git://github.com/dart-lang/clock.git + collection: + git: git://github.com/dart-lang/collection.git + js: + git: + url: git://github.com/dart-lang/sdk.git + path: pkg/js + ref: 2-10-pkgs + matcher: + git: git://github.com/dart-lang/matcher.git + meta: + git: + url: git://github.com/dart-lang/sdk.git + path: pkg/meta + ref: 2-10-pkgs + path: + git: git://github.com/dart-lang/path.git + pedantic: + git: git://github.com/dart-lang/pedantic.git + pool: + git: git://github.com/dart-lang/pool.git + source_maps: + git: git://github.com/dart-lang/source_maps.git + source_map_stack_trace: + git: git://github.com/dart-lang/source_map_stack_trace.git + source_span: + git: git://github.com/dart-lang/source_span.git + stack_trace: + git: git://github.com/dart-lang/stack_trace.git + stream_channel: + git: git://github.com/dart-lang/stream_channel.git + string_scanner: + git: git://github.com/dart-lang/string_scanner.git + term_glyph: + git: git://github.com/dart-lang/term_glyph.git + test_api: + git: + url: git://github.com/dart-lang/test.git + path: pkgs/test_api + test_core: + git: + url: git://github.com/dart-lang/test.git + path: pkgs/test_core + test: + git: + url: git://github.com/dart-lang/test.git + path: pkgs/test + typed_data: + git: git://github.com/dart-lang/typed_data.git diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index d6095332a..a32c35ca3 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -171,7 +171,7 @@ void main() { test('should pass the periodic timer itself to callbacks', () { FakeAsync().run((async) { - Timer constructed; + late Timer constructed; constructed = Timer.periodic(elapseBy, expectAsync1((passed) { expect(passed, same(constructed)); })); @@ -448,9 +448,9 @@ void main() { test('should report the number of pending microtasks', () { FakeAsync().run((async) { expect(async.microtaskCount, 0); - scheduleMicrotask(() => null); + scheduleMicrotask(() {}); expect(async.microtaskCount, 1); - scheduleMicrotask(() => null); + scheduleMicrotask(() {}); expect(async.microtaskCount, 2); async.flushMicrotasks(); expect(async.microtaskCount, 0); @@ -516,7 +516,7 @@ void main() { group('timers', () { test("should become inactive as soon as they're invoked", () { return FakeAsync().run((async) { - Timer timer; + late Timer timer; timer = Timer(elapseBy, expectAsync0(() { expect(timer.isActive, isFalse); })); From 5e8c9791e39a9fe734f8d84a5c364426c21fdfed Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Jul 2020 21:02:56 -0700 Subject: [PATCH 064/113] CI: Test on dev branch --- pkgs/fake_async/.travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml index 187244c87..481e06250 100644 --- a/pkgs/fake_async/.travis.yml +++ b/pkgs/fake_async/.travis.yml @@ -1,7 +1,7 @@ language: dart dart: - - preview/raw/2.10.0-0.2-dev + - dev dart_task: - test: --platform vm,chrome From 9717e248da8aea088b1a52b41c69ef0c0fa31de0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 17 Aug 2020 11:08:50 -0700 Subject: [PATCH 065/113] Use higher version for null safety (dart-lang/fake_async#23) The `1.1.0-nullsafety` version is considered _earlier_ than `1.1.0` which is already published. Use `1.2.0-nullsafety` instead. --- pkgs/fake_async/CHANGELOG.md | 16 +++++++++++----- pkgs/fake_async/pubspec.yaml | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 93b7a4b04..c56fe2fd2 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,12 +1,18 @@ -## 1.1.0-nullsafety +## 1.2.0-nullsafety -* Update to null safety. +Pre-release for the null safety migration of this package. + +Note that `1.2.0` may not be the final stable null safety release version, +we reserve the right to release it as a `2.0.0` breaking change. + +This release will be pinned to only allow pre-release sdk versions starting +from `2.10.0-0`. ## 1.1.0 - * Exposed the `FakeTimer` class as a public class. - * Added `FakeAsync.pendingTimers` which gives access to all pending timers - at the time of the call. +* Exposed the `FakeTimer` class as a public class. +* Added `FakeAsync.pendingTimers` which gives access to all pending timers at + the time of the call. ## 1.0.2 diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 7e05dfd4c..d5ff189c0 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.1.0-nullsafety +version: 1.2.0-nullsafety description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. From 0865f25ca2c11ed9583dd95e7a7be1029e67fa1f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 22 Sep 2020 09:59:37 -0700 Subject: [PATCH 066/113] Prepare for the 2.11 dev SDKs (dart-lang/fake_async#25) Bump the upper bound to allow 2.10 stable and 2.11.0 dev SDK versions. --- pkgs/fake_async/CHANGELOG.md | 4 ++++ pkgs/fake_async/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index c56fe2fd2..b77401158 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.0-nullsafety.1 + +* Allow 2.10 stable and 2.11.0 dev SDK versions. + ## 1.2.0-nullsafety Pre-release for the null safety migration of this package. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index d5ff189c0..81372439c 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.2.0-nullsafety +version: 1.2.0-nullsafety.1 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. @@ -7,7 +7,7 @@ homepage: https://github.com/dart-lang/fake_async environment: # This must remain a tight constraint until nnbd is stable - sdk: '>=2.10.0-0 <2.10.0' + sdk: '>=2.10.0-0 <2.11.0' dependencies: clock: '>=1.1.0-nullsafety <1.1.0' From 45fa456349cabc0af9bb09bad936edab6e091fb7 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Fri, 23 Oct 2020 13:31:07 -0700 Subject: [PATCH 067/113] allow the 2.12 prerelease sdks (dart-lang/fake_async#27) --- pkgs/fake_async/CHANGELOG.md | 4 +++ pkgs/fake_async/pubspec.yaml | 67 +++--------------------------------- 2 files changed, 8 insertions(+), 63 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index b77401158..18bbaf235 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.0-nullsafety.2 + +* Allow prerelease versions of the 2.12 sdk. + ## 1.2.0-nullsafety.1 * Allow 2.10 stable and 2.11.0 dev SDK versions. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 81372439c..6fde3dd18 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.2.0-nullsafety.1 +version: 1.2.0-nullsafety.2 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. @@ -7,71 +7,12 @@ homepage: https://github.com/dart-lang/fake_async environment: # This must remain a tight constraint until nnbd is stable - sdk: '>=2.10.0-0 <2.11.0' + sdk: '>=2.10.0-0 <2.12.0' dependencies: clock: '>=1.1.0-nullsafety <1.1.0' collection: '>=1.15.0-nullsafety <1.15.0' dev_dependencies: - async: ^2.0.0 - test: ^1.0.0 - - -dependency_overrides: - async: - git: git://github.com/dart-lang/async.git - boolean_selector: - git: git://github.com/dart-lang/boolean_selector.git - charcode: - git: git://github.com/dart-lang/charcode.git - clock: - git: git://github.com/dart-lang/clock.git - collection: - git: git://github.com/dart-lang/collection.git - js: - git: - url: git://github.com/dart-lang/sdk.git - path: pkg/js - ref: 2-10-pkgs - matcher: - git: git://github.com/dart-lang/matcher.git - meta: - git: - url: git://github.com/dart-lang/sdk.git - path: pkg/meta - ref: 2-10-pkgs - path: - git: git://github.com/dart-lang/path.git - pedantic: - git: git://github.com/dart-lang/pedantic.git - pool: - git: git://github.com/dart-lang/pool.git - source_maps: - git: git://github.com/dart-lang/source_maps.git - source_map_stack_trace: - git: git://github.com/dart-lang/source_map_stack_trace.git - source_span: - git: git://github.com/dart-lang/source_span.git - stack_trace: - git: git://github.com/dart-lang/stack_trace.git - stream_channel: - git: git://github.com/dart-lang/stream_channel.git - string_scanner: - git: git://github.com/dart-lang/string_scanner.git - term_glyph: - git: git://github.com/dart-lang/term_glyph.git - test_api: - git: - url: git://github.com/dart-lang/test.git - path: pkgs/test_api - test_core: - git: - url: git://github.com/dart-lang/test.git - path: pkgs/test_core - test: - git: - url: git://github.com/dart-lang/test.git - path: pkgs/test - typed_data: - git: git://github.com/dart-lang/typed_data.git + async: ^2.5.0-nullsafety + test: ^1.16.0-nullsafety From 0f20c8265a8a780cb7779c3957edd4abc25a27c8 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 3 Nov 2020 14:41:22 -0800 Subject: [PATCH 068/113] Bump SDK constraints for pub (dart-lang/fake_async#28) Use a 2.12.0 lower bound since pub does not understand allowed experiments for earlier versions. Use a 3.0.0 upper bound to avoid a warning in pub and to give some flexibility in publishing for stable. --- pkgs/fake_async/CHANGELOG.md | 5 +++++ pkgs/fake_async/pubspec.yaml | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 18bbaf235..f9d8edaa9 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.2.0-nullsafety.3 + +* Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release + guidelines. + ## 1.2.0-nullsafety.2 * Allow prerelease versions of the 2.12 sdk. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 6fde3dd18..8a0b26129 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,13 +1,12 @@ name: fake_async -version: 1.2.0-nullsafety.2 +version: 1.2.0-nullsafety.3 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. homepage: https://github.com/dart-lang/fake_async environment: - # This must remain a tight constraint until nnbd is stable - sdk: '>=2.10.0-0 <2.12.0' + sdk: ">=2.12.0-0 <3.0.0" dependencies: clock: '>=1.1.0-nullsafety <1.1.0' From d00d1d3fac2c774fa247205ee9647fdecb24ce42 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Mon, 21 Dec 2020 22:26:46 +0100 Subject: [PATCH 069/113] Migrate to GitHub Actions (dart-lang/fake_async#30) --- .../.github/workflows/test-package.yml | 64 +++++++++++++++++++ pkgs/fake_async/.travis.yml | 17 ----- 2 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 pkgs/fake_async/.github/workflows/test-package.yml delete mode 100644 pkgs/fake_async/.travis.yml diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml new file mode 100644 index 000000000..e55702c22 --- /dev/null +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -0,0 +1,64 @@ +name: Dart CI + +on: + # Run on PRs and pushes to the default branch. + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: "0 0 * * 0" + +env: + PUB_ENVIRONMENT: bot.github + +jobs: + # Check code formatting and static analysis on a single OS (linux) + # against Dart dev. + analyze: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: [dev] + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v0.1 + with: + channel: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Check formatting + run: dart format --output=none --set-exit-if-changed . + if: always() && steps.install.outcome == 'success' + - name: Analyze code + run: dart analyze --fatal-infos + if: always() && steps.install.outcome == 'success' + + # Run tests on a matrix consisting of two dimensions: + # 1. OS: ubuntu-latest, (macos-latest, windows-latest) + # 2. release channel: dev + test: + needs: analyze + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # Add macos-latest and/or windows-latest if relevant for this package. + os: [ubuntu-latest] + sdk: [dev] + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v0.1 + with: + channel: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Run VM tests + run: dart test --platform vm + if: always() && steps.install.outcome == 'success' + - name: Run Chrome tests + run: dart test --platform chrome + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/fake_async/.travis.yml b/pkgs/fake_async/.travis.yml deleted file mode 100644 index 481e06250..000000000 --- a/pkgs/fake_async/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -language: dart - -dart: - - dev - -dart_task: - - test: --platform vm,chrome - - dartfmt - - dartanalyzer: --fatal-warnings --fatal-hints . - -# Only building master means that we don't run two builds for each pull request. -branches: - only: [master] - -cache: - directories: - - $HOME/.pub-cache From ddf8ee146738c38bbbfa0ce0683fa8bbf3315ac0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 2 Feb 2021 13:30:18 -0800 Subject: [PATCH 070/113] Prepare to publish stable null safety (dart-lang/fake_async#31) --- pkgs/fake_async/CHANGELOG.md | 4 ++++ pkgs/fake_async/pubspec.yaml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index f9d8edaa9..4c3a7014b 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.0 + +* Stable release for null safety. + ## 1.2.0-nullsafety.3 * Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 8a0b26129..61d6b029f 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.2.0-nullsafety.3 +version: 1.2.0 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. @@ -9,8 +9,8 @@ environment: sdk: ">=2.12.0-0 <3.0.0" dependencies: - clock: '>=1.1.0-nullsafety <1.1.0' - collection: '>=1.15.0-nullsafety <1.15.0' + clock: ^1.1.0 + collection: ^1.15.0 dev_dependencies: async: ^2.5.0-nullsafety From 7a02cc3a1771afde0f20d897915b82c3bad266ad Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 27 Apr 2021 14:20:23 -0700 Subject: [PATCH 071/113] update ci (dart-lang/fake_async#33) --- pkgs/fake_async/.github/workflows/test-package.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index e55702c22..cdc25d958 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,9 +23,9 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v0.1 + - uses: dart-lang/setup-dart@v1.0 with: - channel: ${{ matrix.sdk }} + sdk: ${{ matrix.sdk }} - id: install name: Install dependencies run: dart pub get @@ -47,12 +47,12 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [dev] + sdk: [2.12.0, dev] steps: - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v0.1 + - uses: dart-lang/setup-dart@v1.0 with: - channel: ${{ matrix.sdk }} + sdk: ${{ matrix.sdk }} - id: install name: Install dependencies run: dart pub get From 47d6a25025b64e91f29d71fe65699f135b75a7c1 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 29 Jun 2021 12:43:24 -0700 Subject: [PATCH 072/113] Add values to FakeTimer tick (dart-lang/fake_async#35) In a real timer the tick count can go up by more than the number of times the callback was invoked if a period is missed. In the FakeAsync case the `tick` matches the number of times the callback was invoked. --- pkgs/fake_async/CHANGELOG.md | 4 ++++ pkgs/fake_async/lib/fake_async.dart | 7 ++++--- pkgs/fake_async/pubspec.yaml | 2 +- pkgs/fake_async/test/fake_async_test.dart | 25 +++++++++++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 4c3a7014b..5d5ea4960 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.0-dev + +* `FakeTimer.tick` will return a value instead of throwing. + ## 1.2.0 * Stable release for null safety. diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 52dd3ba03..1c9a24cf4 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -283,10 +283,10 @@ class FakeTimer implements Timer { /// The current stack trace when this timer was created. final creationStackTrace = StackTrace.current; + var _tick = 0; + @override - int get tick { - throw UnimplementedError('tick'); - } + int get tick => _tick; /// Returns debugging information to try to identify the source of the /// [Timer]. @@ -308,6 +308,7 @@ class FakeTimer implements Timer { /// Fires this timer's callback and updates its state as necessary. void _fire() { assert(isActive); + _tick++; if (isPeriodic) { _callback(this); _nextCall += duration; diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 61d6b029f..f596c221d 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.2.0 +version: 1.3.0-dev description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index a32c35ca3..d57ff419f 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -526,6 +526,31 @@ void main() { expect(timer.isActive, isFalse); }); }); + + test('should increment tick in a non-periodic timer', () { + return FakeAsync().run((async) { + late Timer timer; + timer = Timer(elapseBy, expectAsync0(() { + expect(timer.tick, 1); + })); + + expect(timer.tick, 0); + async.elapse(elapseBy); + }); + }); + + test('should increment tick in a periodic timer', () { + return FakeAsync().run((async) { + final ticks = []; + Timer.periodic( + elapseBy, + expectAsync1((timer) { + ticks.add(timer.tick); + }, count: 2)); + async..elapse(elapseBy)..elapse(elapseBy); + expect(ticks, [1, 2]); + }); + }); }); group('clock', () { From 911dc4f6c4f19744ef2d37310aef825507a32b86 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 14 Jul 2021 11:22:19 -0700 Subject: [PATCH 073/113] Dart format with latest SDK (dart-lang/fake_async#36) --- pkgs/fake_async/test/fake_async_test.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index d57ff419f..cfe5d36d0 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -547,7 +547,9 @@ void main() { expectAsync1((timer) { ticks.add(timer.tick); }, count: 2)); - async..elapse(elapseBy)..elapse(elapseBy); + async + ..elapse(elapseBy) + ..elapse(elapseBy); expect(ticks, [1, 2]); }); }); From dab325fcd38beb574d996386204f902afb2d92dd Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 22 Jul 2021 14:23:30 -0700 Subject: [PATCH 074/113] Move from pedantic to lints package (dart-lang/fake_async#37) --- pkgs/fake_async/analysis_options.yaml | 10 ++-------- pkgs/fake_async/pubspec.yaml | 7 ++++--- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index 55139b7a1..8224a88e0 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -1,14 +1,8 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/recommended.yaml analyzer: strong-mode: implicit-casts: false - enable-experiment: - - non-nullable - errors: - dead_code: error - unused_element: error - unused_import: error - unused_local_variable: error + linter: rules: - always_declare_return_types diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index f596c221d..c99f4f3bb 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -6,12 +6,13 @@ description: >- homepage: https://github.com/dart-lang/fake_async environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: clock: ^1.1.0 collection: ^1.15.0 dev_dependencies: - async: ^2.5.0-nullsafety - test: ^1.16.0-nullsafety + async: ^2.5.0 + lints: ^1.0.0 + test: ^1.16.0 From 8189e343f4ccbb4b5c8dcb58ba1574a034fb0637 Mon Sep 17 00:00:00 2001 From: Tyler <18113850+dshukertjr@users.noreply.github.com> Date: Thu, 4 Nov 2021 03:24:24 +0900 Subject: [PATCH 075/113] Updated example on Readme.md (dart-lang/fake_async#39) --- pkgs/fake_async/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md index 1c1da30ec..fe2c42eb7 100644 --- a/pkgs/fake_async/README.md +++ b/pkgs/fake_async/README.md @@ -22,12 +22,12 @@ void main() { fakeAsync((async) { // All asynchronous features that rely on timing are automatically // controlled by [fakeAsync]. - expect(new Completer().future.timeout(new Duration(seconds: 5)), - throwsA(new isInstanceOf())); + expect(Completer().future.timeout(Duration(seconds: 5)), + throwsA(isA())); // This will cause the timeout above to fire immediately, without waiting // 5 seconds of real time. - async.elapse(new Duration(seconds: 5)); + async.elapse(Duration(seconds: 5)); }); }); } From 00a3c05980658436b8fffed229dbd4f43b28a2fe Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 9 Feb 2022 09:55:58 -0800 Subject: [PATCH 076/113] Allow FakeAsync to disable creation StackTraces in FakeTimers (dart-lang/fake_async#40) --- pkgs/fake_async/CHANGELOG.md | 2 ++ pkgs/fake_async/lib/fake_async.dart | 26 +++++++++++++------- pkgs/fake_async/test/fake_async_test.dart | 29 +++++++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 5d5ea4960..af981175b 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,6 +1,8 @@ ## 1.3.0-dev * `FakeTimer.tick` will return a value instead of throwing. +* `FakeAsync.includeTimerStackTrace` allows controlling whether timers created + with a FakeAsync will include a creation Stack Trace. ## 1.2.0 diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 1c9a24cf4..0f3287ebf 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -57,6 +57,10 @@ class FakeAsync { Duration get elapsed => _elapsed; var _elapsed = Duration.zero; + /// Whether Timers created by this FakeAsync will include a creation stack + /// trace in [FakeAsync.pendingTimersDebugString]. + final bool includeTimerStackTrace; + /// The fake time at which the current call to [elapse] will finish running. /// /// This is `null` if there's no current call to [elapse]. @@ -98,7 +102,7 @@ class FakeAsync { /// /// Note: it's usually more convenient to use [fakeAsync] rather than creating /// a [FakeAsync] object and calling [run] manually. - FakeAsync({DateTime? initialTime}) { + FakeAsync({DateTime? initialTime, this.includeTimerStackTrace = true}) { var nonNullInitialTime = initialTime ?? clock.now(); _clock = Clock(() => nonNullInitialTime.add(elapsed)); } @@ -245,7 +249,8 @@ class FakeAsync { /// Creates a new timer controlled by `this` that fires [callback] after /// [duration] (or every [duration] if [periodic] is `true`). Timer _createTimer(Duration duration, Function callback, bool periodic) { - var timer = FakeTimer._(duration, callback, periodic, this); + var timer = FakeTimer._(duration, callback, periodic, this, + includeStackTrace: includeTimerStackTrace); _timers.add(timer); return timer; } @@ -281,7 +286,11 @@ class FakeTimer implements Timer { late Duration _nextCall; /// The current stack trace when this timer was created. - final creationStackTrace = StackTrace.current; + /// + /// If [FakeAsync.includeTimerStackTrace] is set to false then accessing + /// this field will throw a [TypeError]. + StackTrace get creationStackTrace => _creationStackTrace!; + final StackTrace? _creationStackTrace; var _tick = 0; @@ -290,12 +299,13 @@ class FakeTimer implements Timer { /// Returns debugging information to try to identify the source of the /// [Timer]. - String get debugString => - 'Timer (duration: $duration, periodic: $isPeriodic), created:\n' - '$creationStackTrace'; + String get debugString => 'Timer (duration: $duration, periodic: $isPeriodic)' + '${_creationStackTrace != null ? ', created:\n$creationStackTrace' : ''}'; - FakeTimer._(Duration duration, this._callback, this.isPeriodic, this._async) - : duration = duration < Duration.zero ? Duration.zero : duration { + FakeTimer._(Duration duration, this._callback, this.isPeriodic, this._async, + {bool includeStackTrace = true}) + : duration = duration < Duration.zero ? Duration.zero : duration, + _creationStackTrace = includeStackTrace ? StackTrace.current : null { _nextCall = _async._elapsed + this.duration; } diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index cfe5d36d0..8b490ae32 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -511,6 +511,35 @@ void main() { expect(periodic.debugString, contains(thisFileName)); }); }); + + test( + 'should report debugging information of pending timers excluding stack traces', + () { + FakeAsync(includeTimerStackTrace: false).run((fakeAsync) { + expect(fakeAsync.pendingTimers, isEmpty); + var nonPeriodic = Timer(const Duration(seconds: 1), () {}) as FakeTimer; + var periodic = + Timer.periodic(const Duration(seconds: 2), (Timer timer) {}) + as FakeTimer; + final debugInfo = fakeAsync.pendingTimers; + expect(debugInfo.length, 2); + expect( + debugInfo, + containsAll([ + nonPeriodic, + periodic, + ]), + ); + + const thisFileName = 'fake_async_test.dart'; + expect(nonPeriodic.debugString, contains(':01.0')); + expect(nonPeriodic.debugString, contains('periodic: false')); + expect(nonPeriodic.debugString, isNot(contains(thisFileName))); + expect(periodic.debugString, contains(':02.0')); + expect(periodic.debugString, contains('periodic: true')); + expect(periodic.debugString, isNot(contains(thisFileName))); + }); + }); }); group('timers', () { From 4c2fd7a1e25a823a899a39337ddd806b50ff6756 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 16 Feb 2022 12:13:22 -0800 Subject: [PATCH 077/113] Enable and fix lines_longer_than_80_chars (dart-lang/fake_async#42) I missed a long string in a recent PR. Enable the lint to avoid relying on human reviewers. --- pkgs/fake_async/analysis_options.yaml | 1 + pkgs/fake_async/test/fake_async_test.dart | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index 8224a88e0..37f1b40c9 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -43,6 +43,7 @@ linter: - join_return_with_assignment - library_names - library_prefixes + - lines_longer_than_80_chars - list_remove_unrelated_type - literal_only_boolean_expressions - no_adjacent_strings_in_list diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 8b490ae32..671b07d2b 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -513,8 +513,8 @@ void main() { }); test( - 'should report debugging information of pending timers excluding stack traces', - () { + 'should report debugging information of pending timers excluding ' + 'stack traces', () { FakeAsync(includeTimerStackTrace: false).run((fakeAsync) { expect(fakeAsync.pendingTimers, isEmpty); var nonPeriodic = Timer(const Duration(seconds: 1), () {}) as FakeTimer; From e21f44b4729f3b025d7f99aa6a692daf33c09df2 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 16 Mar 2022 11:50:36 -0700 Subject: [PATCH 078/113] Prepare to publish (dart-lang/fake_async#44) --- pkgs/fake_async/CHANGELOG.md | 2 +- pkgs/fake_async/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index af981175b..0f531aeb4 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.3.0-dev +## 1.3.0 * `FakeTimer.tick` will return a value instead of throwing. * `FakeAsync.includeTimerStackTrace` allows controlling whether timers created diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index c99f4f3bb..b562c55e2 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.3.0-dev +version: 1.3.0 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. From e6f08a57558bd9bac3a839d290f464756e3802fa Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 20 Apr 2022 14:52:39 -0700 Subject: [PATCH 079/113] Switch from homepage to repository in pubspec (dart-lang/fake_async#45) --- pkgs/fake_async/CHANGELOG.md | 2 ++ pkgs/fake_async/pubspec.yaml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 0f531aeb4..66fac1eb5 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.3.1-dev + ## 1.3.0 * `FakeTimer.tick` will return a value instead of throwing. diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index b562c55e2..40e5054ff 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,9 +1,9 @@ name: fake_async -version: 1.3.0 +version: 1.3.1-dev description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. -homepage: https://github.com/dart-lang/fake_async +repository: https://github.com/dart-lang/fake_async environment: sdk: ">=2.12.0 <3.0.0" From cf23898f0e4da0753274b191d8d1f20cbf93c47a Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 15 Jun 2022 10:59:30 -0700 Subject: [PATCH 080/113] prep to publish 1.3.1 --- pkgs/fake_async/CHANGELOG.md | 4 +++- pkgs/fake_async/README.md | 4 ++++ pkgs/fake_async/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 66fac1eb5..41ca02b30 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,4 +1,6 @@ -## 1.3.1-dev +## 1.3.1 + +* Populate the pubspec `repository` field. ## 1.3.0 diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md index fe2c42eb7..c24148248 100644 --- a/pkgs/fake_async/README.md +++ b/pkgs/fake_async/README.md @@ -1,3 +1,7 @@ +[![Dart CI](https://github.com/dart-lang/fake_async/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/fake_async/actions/workflows/test-package.yml) +[![pub package](https://img.shields.io/pub/v/fake_async.svg)](https://pub.dev/packages/fake_async) +[![package publisher](https://img.shields.io/pub/publisher/fake_async.svg)](https://pub.dev/packages/fake_async/publisher) + This package provides a [`FakeAsync`][] class, which makes it easy to deterministically test code that uses asynchronous features like `Future`s, `Stream`s, `Timer`s, and microtasks. It creates an environment in which the user diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 40e5054ff..1445d9e0a 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,5 +1,5 @@ name: fake_async -version: 1.3.1-dev +version: 1.3.1 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. From 05cb18542d76e315ef4b69527b526f400544d18b Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sun, 24 Jul 2022 21:17:28 -0700 Subject: [PATCH 081/113] Fix analysis options (dart-lang/fake_async#48) --- pkgs/fake_async/analysis_options.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index 37f1b40c9..3c6340b74 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -38,7 +38,6 @@ linter: - file_names - hash_and_equals - implementation_imports - - invariant_booleans - iterable_contains_unrelated_type - join_return_with_assignment - library_names From 5e307a3b175bcb127d633b87c1201bc402ff2bc0 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 7 Sep 2022 16:32:52 -0700 Subject: [PATCH 082/113] Update CONTRIBUTING.md --- pkgs/fake_async/CONTRIBUTING.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkgs/fake_async/CONTRIBUTING.md b/pkgs/fake_async/CONTRIBUTING.md index 615b92ff9..2e76c7cbc 100644 --- a/pkgs/fake_async/CONTRIBUTING.md +++ b/pkgs/fake_async/CONTRIBUTING.md @@ -38,11 +38,9 @@ Before you send your pull request, make sure all the tests pass! ### File headers -All files in the project must start with the following header. - - // Copyright (c) 2017, 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. +When creating new source files, please copy the file header from +https://github.com/dart-lang/fake_async/blob/master/lib/fake_async.dart and +update the year for the copyright to the current year. ### The small print From 47d571ace6604b7c4fa4ec39ea9a958614df2ae3 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 9 Nov 2022 15:22:09 -0800 Subject: [PATCH 083/113] blast_repo fixes (dart-lang/fake_async#53) Dependabot GitHub Action --- pkgs/fake_async/.github/dependabot.yml | 9 +++++++++ pkgs/fake_async/.github/workflows/test-package.yml | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 pkgs/fake_async/.github/dependabot.yml diff --git a/pkgs/fake_async/.github/dependabot.yml b/pkgs/fake_async/.github/dependabot.yml new file mode 100644 index 000000000..1603cdd9e --- /dev/null +++ b/pkgs/fake_async/.github/dependabot.yml @@ -0,0 +1,9 @@ +# Dependabot configuration file. +# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index cdc25d958..68f255c74 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,8 +22,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.0 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install @@ -49,8 +49,8 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@v2 - - uses: dart-lang/setup-dart@v1.0 + - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} - id: install From 9afd633df48509cc8176fd00a1848a9dd7364152 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:27:44 -0800 Subject: [PATCH 084/113] Bump actions/checkout from 3.1.0 to 3.2.0 (dart-lang/fake_async#54) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8...755da8c3cf115ac066823e79a1e1788f8940201b) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 68f255c74..c3560007f 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} From d89c3046ae1b6d5c2f188ce4f28713a293700cc9 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 9 Jan 2023 14:58:28 -0800 Subject: [PATCH 085/113] Migrate from no-implicit-casts to strict-casts (dart-lang/fake_async#55) --- pkgs/fake_async/analysis_options.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index 3c6340b74..ad43c639e 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -1,7 +1,7 @@ include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: false + language: + strict-casts: true linter: rules: From 2ca30433d722bbf03be4e00c411a978076dca937 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 08:06:02 -0800 Subject: [PATCH 086/113] Bump dart-lang/setup-dart from 1.3 to 1.4 (dart-lang/fake_async#56) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.3 to 1.4. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/6a218f2413a3e78e9087f638a238f6b40893203d...a57a6c04cf7d4840e88432aad6281d1e125f0d46) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index c3560007f..ce5f2d2b1 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [2.12.0, dev] steps: - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d + - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} - id: install From ff0cb2443b45d27fe733642e11c64643b2df5d40 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 08:14:50 -0800 Subject: [PATCH 087/113] Bump actions/checkout from 3.2.0 to 3.3.0 (dart-lang/fake_async#57) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.2.0 to 3.3.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/755da8c3cf115ac066823e79a1e1788f8940201b...ac593985615ec2ede58e132d2e21d2b1cbd6127c) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index ce5f2d2b1..70fb7ec43 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.12.0, dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} From bbb7d6e357bb355b12d47f25bf403b3daff79d9f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sun, 12 Feb 2023 17:17:00 -0800 Subject: [PATCH 088/113] Fix new analysis warnings, move to dart_flutter_team_lints (dart-lang/fake_async#58) --- .../.github/workflows/test-package.yml | 2 +- pkgs/fake_async/CHANGELOG.md | 4 + pkgs/fake_async/analysis_options.yaml | 79 +---------- pkgs/fake_async/lib/fake_async.dart | 14 +- pkgs/fake_async/pubspec.yaml | 6 +- pkgs/fake_async/test/fake_async_test.dart | 126 +++++++++--------- 6 files changed, 84 insertions(+), 147 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 70fb7ec43..5a3d9cfe1 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.12.0, dev] + sdk: [2.17.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 41ca02b30..f1db7b791 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.2-dev + +* Require Dart 2.17. + ## 1.3.1 * Populate the pubspec `repository` field. diff --git a/pkgs/fake_async/analysis_options.yaml b/pkgs/fake_async/analysis_options.yaml index ad43c639e..c0b72ff33 100644 --- a/pkgs/fake_async/analysis_options.yaml +++ b/pkgs/fake_async/analysis_options.yaml @@ -1,95 +1,22 @@ -include: package:lints/recommended.yaml +include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: language: strict-casts: true linter: rules: - - always_declare_return_types - - annotate_overrides - avoid_bool_literals_in_conditional_expressions - avoid_classes_with_only_static_members - - avoid_empty_else - - avoid_function_literals_in_foreach_calls - - avoid_init_to_null - - avoid_null_checks_in_equality_operators - - avoid_relative_lib_imports - - avoid_renaming_method_parameters - - avoid_return_types_on_setters - - avoid_returning_null - - avoid_returning_null_for_future - - avoid_returning_null_for_void - avoid_returning_this - - avoid_shadowing_type_parameters - - avoid_single_cascade_in_expression_statements - - avoid_types_as_parameter_names - avoid_unused_constructor_parameters - - await_only_futures - - camel_case_types - cancel_subscriptions - cascade_invocations - comment_references - - constant_identifier_names - - control_flow_in_finally - - directives_ordering - - empty_catches - - empty_constructor_bodies - - empty_statements - - file_names - - hash_and_equals - - implementation_imports - - iterable_contains_unrelated_type - join_return_with_assignment - - library_names - - library_prefixes - - lines_longer_than_80_chars - - list_remove_unrelated_type - literal_only_boolean_expressions - no_adjacent_strings_in_list - - no_duplicate_case_values - - non_constant_identifier_names - - null_closures - - omit_local_variable_types - - only_throw_errors - - overridden_fields - package_api_docs - - package_names - - package_prefixed_library_names - - prefer_adjacent_string_concatenation - # Wait for oldest supported SDK to be 2.2 - #- prefer_collection_literals - - prefer_conditional_assignment - #- prefer_const_constructors - - prefer_contains - - prefer_equal_for_default_values - - prefer_final_fields - #- prefer_final_locals - - prefer_generic_function_type_aliases - - prefer_initializing_formals - - prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - - prefer_null_aware_operators - - prefer_single_quotes - - prefer_typing_uninitialized_variables - - recursive_getters - - slash_for_doc_comments + - prefer_const_constructors + - prefer_final_locals - test_types_in_equals - - throw_in_finally - - type_init_formals - - unawaited_futures - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_new - - unnecessary_null_aware_assignments - - unnecessary_parenthesis - - unnecessary_statements - - unnecessary_this - - unrelated_type_equality_checks - - use_function_type_syntax_for_parameters - - use_rethrow_when_possible - - valid_regexps - - void_checks diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 0f3287ebf..415cb967e 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -103,7 +103,7 @@ class FakeAsync { /// Note: it's usually more convenient to use [fakeAsync] rather than creating /// a [FakeAsync] object and calling [run] manually. FakeAsync({DateTime? initialTime, this.includeTimerStackTrace = true}) { - var nonNullInitialTime = initialTime ?? clock.now(); + final nonNullInitialTime = initialTime ?? clock.now(); _clock = Clock(() => nonNullInitialTime.add(elapsed)); } @@ -156,7 +156,7 @@ class FakeAsync { } _elapsed += duration; - var elapsingTo = _elapsingTo; + final elapsingTo = _elapsingTo; if (elapsingTo != null && _elapsed > elapsingTo) _elapsingTo = _elapsed; } @@ -169,7 +169,7 @@ class FakeAsync { /// The [`clock`][] property will be set to a clock that reports the fake /// elapsed time. By default, it starts at the time the [FakeAsync] was /// created (according to [`clock.now()`][]), but this can be controlled by - /// passing `initialTime` to [new FakeAsync]. + /// passing `initialTime` to [FakeAsync.new]. /// /// [`clock`]: https://www.dartdocs.org/documentation/clock/latest/clock/clock.html /// [`clock.now()`]: https://www.dartdocs.org/documentation/clock/latest/clock/Clock/now.html @@ -210,7 +210,7 @@ class FakeAsync { void flushTimers( {Duration timeout = const Duration(hours: 1), bool flushPeriodicTimers = true}) { - var absoluteTimeout = _elapsed + timeout; + final absoluteTimeout = _elapsed + timeout; _fireTimersWhile((timer) { if (timer._nextCall > absoluteTimeout) { // TODO(nweiz): Make this a [TimeoutException]. @@ -237,7 +237,7 @@ class FakeAsync { for (;;) { if (_timers.isEmpty) break; - var timer = minBy(_timers, (FakeTimer timer) => timer._nextCall)!; + final timer = minBy(_timers, (FakeTimer timer) => timer._nextCall)!; if (!predicate(timer)) break; _elapseTo(timer._nextCall); @@ -249,7 +249,7 @@ class FakeAsync { /// Creates a new timer controlled by `this` that fires [callback] after /// [duration] (or every [duration] if [periodic] is `true`). Timer _createTimer(Duration duration, Function callback, bool periodic) { - var timer = FakeTimer._(duration, callback, periodic, this, + final timer = FakeTimer._(duration, callback, periodic, this, includeStackTrace: includeTimerStackTrace); _timers.add(timer); return timer; @@ -320,10 +320,12 @@ class FakeTimer implements Timer { assert(isActive); _tick++; if (isPeriodic) { + // ignore: avoid_dynamic_calls _callback(this); _nextCall += duration; } else { cancel(); + // ignore: avoid_dynamic_calls _callback(); } } diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 1445d9e0a..2d87cd2e5 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,12 +1,12 @@ name: fake_async -version: 1.3.1 +version: 1.3.2-dev description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. repository: https://github.com/dart-lang/fake_async environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.17.0 <3.0.0" dependencies: clock: ^1.1.0 @@ -14,5 +14,5 @@ dependencies: dev_dependencies: async: ^2.5.0 - lints: ^1.0.0 + dart_flutter_team_lints: ^0.1.0 test: ^1.16.0 diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 671b07d2b..e0d78bb3c 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -19,8 +19,8 @@ import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; void main() { - var initialTime = DateTime(2000); - var elapseBy = Duration(days: 1); + final initialTime = DateTime(2000); + final elapseBy = const Duration(days: 1); test('should set initial time', () { expect(FakeAsync().getClock(initialTime).now(), initialTime); @@ -33,12 +33,12 @@ void main() { }); test('should elapse time by the specified amount', () { - var async = FakeAsync()..elapseBlocking(elapseBy); + final async = FakeAsync()..elapseBlocking(elapseBy); expect(async.elapsed, elapseBy); }); test('should throw when called with a negative duration', () { - expect(() => FakeAsync().elapseBlocking(Duration(days: -1)), + expect(() => FakeAsync().elapseBlocking(const Duration(days: -1)), throwsArgumentError); }); }); @@ -52,7 +52,8 @@ void main() { }); test('should throw ArgumentError when called with a negative duration', () { - expect(() => FakeAsync().elapse(Duration(days: -1)), throwsArgumentError); + expect(() => FakeAsync().elapse(const Duration(days: -1)), + throwsArgumentError); }); test('should throw when called before previous call is complete', () { @@ -88,7 +89,7 @@ void main() { expect(async.elapsed, elapseBy ~/ 2); })); - var periodicCalledAt = []; + final periodicCalledAt = []; Timer.periodic( elapseBy ~/ 2, (_) => periodicCalledAt.add(async.elapsed)); @@ -120,7 +121,7 @@ void main() { test('should call timers occurring at the same time in FIFO order', () { FakeAsync().run((async) { - var log = []; + final log = []; Timer(elapseBy ~/ 2, () => log.add('1')); Timer(elapseBy ~/ 2, () => log.add('2')); async.elapse(elapseBy); @@ -130,7 +131,7 @@ void main() { test('should maintain FIFO order even with periodic timers', () { FakeAsync().run((async) { - var log = []; + final log = []; Timer.periodic(elapseBy ~/ 2, (_) => log.add('periodic 1')); Timer(elapseBy ~/ 2, () => log.add('delayed 1')); Timer(elapseBy, () => log.add('delayed 2')); @@ -184,28 +185,28 @@ void main() { scheduleMicrotask(expectAsync0(() { expect(async.elapsed, Duration.zero); })); - async.elapse(Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); }); }); test('should add event before advancing time', () { FakeAsync().run((async) { - var controller = StreamController(); + final controller = StreamController(); expect(controller.stream.first.then((_) { expect(async.elapsed, Duration.zero); }), completes); controller.add(null); - async.elapse(Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); }); }); test('should increase negative duration timers to zero duration', () { FakeAsync().run((async) { - var negativeDuration = Duration(days: -1); + final negativeDuration = const Duration(days: -1); Timer(negativeDuration, expectAsync0(() { expect(async.elapsed, Duration.zero); })); - async.elapse(Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); }); }); @@ -220,7 +221,7 @@ void main() { group('isActive', () { test('should be false after timer is run', () { FakeAsync().run((async) { - var timer = Timer(elapseBy ~/ 2, () {}); + final timer = Timer(elapseBy ~/ 2, () {}); async.elapse(elapseBy); expect(timer.isActive, isFalse); }); @@ -228,7 +229,7 @@ void main() { test('should be true after periodic timer is run', () { FakeAsync().run((async) { - var timer = Timer.periodic(elapseBy ~/ 2, (_) {}); + final timer = Timer.periodic(elapseBy ~/ 2, (_) {}); async.elapse(elapseBy); expect(timer.isActive, isTrue); }); @@ -236,7 +237,7 @@ void main() { test('should be false after timer is canceled', () { FakeAsync().run((async) { - var timer = Timer(elapseBy ~/ 2, () {})..cancel(); + final timer = Timer(elapseBy ~/ 2, () {})..cancel(); expect(timer.isActive, isFalse); }); }); @@ -258,9 +259,9 @@ void main() { test('should work with Future.timeout', () { FakeAsync().run((async) { - var completer = Completer(); + final completer = Completer(); expect(completer.future.timeout(elapseBy ~/ 2), - throwsA(TypeMatcher())); + throwsA(const TypeMatcher())); async.elapse(elapseBy); completer.complete(); }); @@ -272,28 +273,28 @@ void main() { // See https://code.google.com/p/dart/issues/detail?id=18149 test('should work with Stream.periodic', () { FakeAsync().run((async) { - expect(Stream.periodic(Duration(minutes: 1), (i) => i), + expect(Stream.periodic(const Duration(minutes: 1), (i) => i), emitsInOrder([0, 1, 2])); - async.elapse(Duration(minutes: 3)); + async.elapse(const Duration(minutes: 3)); }); }); test('should work with Stream.timeout', () { FakeAsync().run((async) { - var controller = StreamController(); - var timed = controller.stream.timeout(Duration(minutes: 2)); + final controller = StreamController(); + final timed = controller.stream.timeout(const Duration(minutes: 2)); - var events = []; - var errors = []; + final events = []; + final errors = []; timed.listen(events.add, onError: errors.add); controller.add(0); - async.elapse(Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); expect(events, [0]); - async.elapse(Duration(minutes: 1)); + async.elapse(const Duration(minutes: 1)); expect(errors, hasLength(1)); - expect(errors.first, TypeMatcher()); + expect(errors.first, const TypeMatcher()); }); }); }); @@ -309,7 +310,7 @@ void main() { test('should flush microtasks scheduled by microtasks in order', () { FakeAsync().run((async) { - var log = []; + final log = []; scheduleMicrotask(() { log.add(1); scheduleMicrotask(() => log.add(3)); @@ -323,10 +324,10 @@ void main() { test('should not run timers', () { FakeAsync().run((async) { - var log = []; + final log = []; scheduleMicrotask(() => log.add(1)); Timer.run(() => log.add(2)); - Timer.periodic(Duration(seconds: 1), (_) => log.add(2)); + Timer.periodic(const Duration(seconds: 1), (_) => log.add(2)); async.flushMicrotasks(); expect(log, [1]); @@ -337,7 +338,7 @@ void main() { group('flushTimers', () { test('should flush timers in FIFO order', () { FakeAsync().run((async) { - var log = []; + final log = []; Timer.run(() { log.add(1); Timer(elapseBy, () => log.add(3)); @@ -354,9 +355,9 @@ void main() { 'should run collateral periodic timers with non-periodic first if ' 'scheduled first', () { FakeAsync().run((async) { - var log = []; - Timer(Duration(seconds: 2), () => log.add('delayed')); - Timer.periodic(Duration(seconds: 1), (_) => log.add('periodic')); + final log = []; + Timer(const Duration(seconds: 2), () => log.add('delayed')); + Timer.periodic(const Duration(seconds: 1), (_) => log.add('periodic')); async.flushTimers(flushPeriodicTimers: false); expect(log, ['periodic', 'delayed', 'periodic']); @@ -367,9 +368,9 @@ void main() { 'should run collateral periodic timers with periodic first ' 'if scheduled first', () { FakeAsync().run((async) { - var log = []; - Timer.periodic(Duration(seconds: 1), (_) => log.add('periodic')); - Timer(Duration(seconds: 2), () => log.add('delayed')); + final log = []; + Timer.periodic(const Duration(seconds: 1), (_) => log.add('periodic')); + Timer(const Duration(seconds: 2), () => log.add('delayed')); async.flushTimers(flushPeriodicTimers: false); expect(log, ['periodic', 'periodic', 'delayed']); @@ -392,14 +393,14 @@ void main() { FakeAsync().run((async) { var count = 0; void createTimer() { - Timer(Duration(minutes: 30), () { + Timer(const Duration(minutes: 30), () { count++; createTimer(); }); } createTimer(); - expect(() => async.flushTimers(timeout: Duration(hours: 2)), + expect(() => async.flushTimers(timeout: const Duration(hours: 2)), throwsStateError); expect(count, 4); }); @@ -407,8 +408,9 @@ void main() { test('should time out periodic timers', () { FakeAsync().run((async) { - Timer.periodic(Duration(minutes: 30), expectAsync1((_) {}, count: 2)); - expect(() => async.flushTimers(timeout: Duration(hours: 1)), + Timer.periodic( + const Duration(minutes: 30), expectAsync1((_) {}, count: 2)); + expect(() => async.flushTimers(timeout: const Duration(hours: 1)), throwsStateError); }); }); @@ -416,11 +418,11 @@ void main() { test('should flush periodic timers', () { FakeAsync().run((async) { var count = 0; - Timer.periodic(Duration(minutes: 30), (timer) { + Timer.periodic(const Duration(minutes: 30), (timer) { if (count == 3) timer.cancel(); count++; }); - async.flushTimers(timeout: Duration(hours: 20)); + async.flushTimers(timeout: const Duration(hours: 20)); expect(count, 4); }); }); @@ -429,7 +431,7 @@ void main() { FakeAsync().run((async) { var count = 0; void createTimer() { - Timer(Duration(minutes: 30), () { + Timer(const Duration(minutes: 30), () { count++; if (count < 4) createTimer(); }); @@ -437,8 +439,8 @@ void main() { createTimer(); async - ..elapse(Duration(hours: 1)) - ..flushTimers(timeout: Duration(hours: 1)); + ..elapse(const Duration(hours: 1)) + ..flushTimers(timeout: const Duration(hours: 1)); expect(count, 4); }); }); @@ -460,11 +462,11 @@ void main() { test('it should report the number of pending periodic timers', () { FakeAsync().run((async) { expect(async.periodicTimerCount, 0); - var timer = Timer.periodic(Duration(minutes: 30), (_) {}); + final timer = Timer.periodic(const Duration(minutes: 30), (_) {}); expect(async.periodicTimerCount, 1); - Timer.periodic(Duration(minutes: 20), (_) {}); + Timer.periodic(const Duration(minutes: 20), (_) {}); expect(async.periodicTimerCount, 2); - async.elapse(Duration(minutes: 20)); + async.elapse(const Duration(minutes: 20)); expect(async.periodicTimerCount, 2); timer.cancel(); expect(async.periodicTimerCount, 1); @@ -474,11 +476,11 @@ void main() { test('it should report the number of pending non periodic timers', () { FakeAsync().run((async) { expect(async.nonPeriodicTimerCount, 0); - var timer = Timer(Duration(minutes: 30), () {}); + final timer = Timer(const Duration(minutes: 30), () {}); expect(async.nonPeriodicTimerCount, 1); - Timer(Duration(minutes: 20), () {}); + Timer(const Duration(minutes: 20), () {}); expect(async.nonPeriodicTimerCount, 2); - async.elapse(Duration(minutes: 25)); + async.elapse(const Duration(minutes: 25)); expect(async.nonPeriodicTimerCount, 1); timer.cancel(); expect(async.nonPeriodicTimerCount, 0); @@ -488,8 +490,9 @@ void main() { test('should report debugging information of pending timers', () { FakeAsync().run((fakeAsync) { expect(fakeAsync.pendingTimers, isEmpty); - var nonPeriodic = Timer(const Duration(seconds: 1), () {}) as FakeTimer; - var periodic = + final nonPeriodic = + Timer(const Duration(seconds: 1), () {}) as FakeTimer; + final periodic = Timer.periodic(const Duration(seconds: 2), (Timer timer) {}) as FakeTimer; final debugInfo = fakeAsync.pendingTimers; @@ -517,8 +520,9 @@ void main() { 'stack traces', () { FakeAsync(includeTimerStackTrace: false).run((fakeAsync) { expect(fakeAsync.pendingTimers, isEmpty); - var nonPeriodic = Timer(const Duration(seconds: 1), () {}) as FakeTimer; - var periodic = + final nonPeriodic = + Timer(const Duration(seconds: 1), () {}) as FakeTimer; + final periodic = Timer.periodic(const Duration(seconds: 2), (Timer timer) {}) as FakeTimer; final debugInfo = fakeAsync.pendingTimers; @@ -587,7 +591,7 @@ void main() { group('clock', () { test('updates following elapse()', () { FakeAsync().run((async) { - var before = clock.now(); + final before = clock.now(); async.elapse(elapseBy); expect(clock.now(), before.add(elapseBy)); }); @@ -595,7 +599,7 @@ void main() { test('updates following elapseBlocking()', () { FakeAsync().run((async) { - var before = clock.now(); + final before = clock.now(); async.elapseBlocking(elapseBy); expect(clock.now(), before.add(elapseBy)); }); @@ -603,7 +607,7 @@ void main() { group('starts at', () { test('the time at which the FakeAsync was created', () { - var start = DateTime.now(); + final start = DateTime.now(); FakeAsync().run((async) { expect(clock.now(), _closeToTime(start)); async.elapse(elapseBy); @@ -612,7 +616,7 @@ void main() { }); test('the value of clock.now()', () { - var start = DateTime(1990, 8, 11); + final start = DateTime(1990, 8, 11); withClock(Clock.fixed(start), () { FakeAsync().run((async) { expect(clock.now(), start); @@ -623,7 +627,7 @@ void main() { }); test('an explicit value', () { - var start = DateTime(1990, 8, 11); + final start = DateTime(1990, 8, 11); FakeAsync(initialTime: start).run((async) { expect(clock.now(), start); async.elapse(elapseBy); From f489de57baaae85f1c8ad288626d4339bf16249a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:36:26 -0700 Subject: [PATCH 089/113] Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (dart-lang/fake_async#60) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/a57a6c04cf7d4840e88432aad6281d1e125f0d46...d6a63dab3335f427404425de0fbfed4686d93c4f) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 5a3d9cfe1..47be07541 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [2.17.0, dev] steps: - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 + - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} - id: install From d7018f909d11b20df25bac50c3fd583f4f6372e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:57:40 -0700 Subject: [PATCH 090/113] Bump actions/checkout from 3.3.0 to 3.5.0 (dart-lang/fake_async#59) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.3.0 to 3.5.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/ac593985615ec2ede58e132d2e21d2b1cbd6127c...8f4b7f84864484a7bf31766abe9204da3cbe65b3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 47be07541..35a98b0c1 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 7ec1f46dd5db4e5ceed9ed883ef57054f322ced5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 12:24:30 -0700 Subject: [PATCH 091/113] Bump actions/checkout from 3.5.0 to 3.5.2 (dart-lang/fake_async#61) Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.0 to 3.5.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/8f4b7f84864484a7bf31766abe9204da3cbe65b3...8e5e7e5ab8b370d6c329ec480221332ada57f0ab) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 35a98b0c1..4bad718a0 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.17.0, dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 503bdf3eaf8f8e5d6558ff4dcec69ab825290c2b Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 17 May 2023 11:10:26 -0700 Subject: [PATCH 092/113] blast_repo fixes (dart-lang/fake_async#62) dependabot --- pkgs/fake_async/.github/dependabot.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/fake_async/.github/dependabot.yml b/pkgs/fake_async/.github/dependabot.yml index 1603cdd9e..725f03af2 100644 --- a/pkgs/fake_async/.github/dependabot.yml +++ b/pkgs/fake_async/.github/dependabot.yml @@ -3,7 +3,9 @@ version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: github-actions + directory: / schedule: - interval: "monthly" + interval: monthly + labels: + - autosubmit From bfc1574883cf036605e81d7755814ef1a4421de8 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 31 May 2023 13:48:04 -0700 Subject: [PATCH 093/113] Require Dart 3.0, update lints (dart-lang/fake_async#63) --- pkgs/fake_async/.github/workflows/test-package.yml | 2 +- pkgs/fake_async/CHANGELOG.md | 4 ++-- pkgs/fake_async/pubspec.yaml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 4bad718a0..6e9fc3911 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.17.0, dev] + sdk: [3.0.0, dev] steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index f1db7b791..7e998ab6d 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,6 +1,6 @@ -## 1.3.2-dev +## 1.3.2-wip -* Require Dart 2.17. +* Require Dart 3.0.0 ## 1.3.1 diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 2d87cd2e5..51cc15369 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,12 +1,12 @@ name: fake_async -version: 1.3.2-dev +version: 1.3.2-wip description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. repository: https://github.com/dart-lang/fake_async environment: - sdk: ">=2.17.0 <3.0.0" + sdk: ^3.0.0 dependencies: clock: ^1.1.0 @@ -14,5 +14,5 @@ dependencies: dev_dependencies: async: ^2.5.0 - dart_flutter_team_lints: ^0.1.0 + dart_flutter_team_lints: ^1.0.0 test: ^1.16.0 From 7ba70ebe3ad28aa371ea328e1f1370addba8241b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 14:18:19 +0000 Subject: [PATCH 094/113] Bump actions/checkout from 3.5.2 to 3.5.3 (dart-lang/fake_async#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.2 to 3.5.3.
Release notes

Sourced from actions/checkout's releases.

v3.5.3

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v3.5.3

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

v2.3.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.2&new-version=3.5.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 6e9fc3911..7ad5f74e0 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 7f9e6211c04992a7434d52abf992955a6facfeaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:24:04 +0000 Subject: [PATCH 095/113] Bump actions/checkout from 3.5.3 to 3.6.0 (dart-lang/fake_async#66) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
Release notes

Sourced from actions/checkout's releases.

v3.6.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3.5.3...v3.6.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

v3.0.1

v3.0.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.5.3&new-version=3.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 7ad5f74e0..24d44c9ac 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 588b8e69dd89141c0d346f9834ce3bc4a52467f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 14:47:04 +0000 Subject: [PATCH 096/113] Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (dart-lang/fake_async#67) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.0 to 1.5.1.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/fake_async#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/fake_async#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

  • Added a flavor option setup.sh to allow downloading unpublished builds.

v1.0.0

  • Promoted to 1.0 stable.

v0.5

  • Fixed a Windows pub global activate path issue.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.5.0&new-version=1.5.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 24d44c9ac..b187c4637 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f + - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f + - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} - id: install From 4f579e30227f887276487a534340b1b7247b1f8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 18:39:48 +0000 Subject: [PATCH 097/113] Bump actions/checkout from 3.6.0 to 4.1.0 (dart-lang/fake_async#68) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 3.6.0 to 4.1.0.
Release notes

Sourced from actions/checkout's releases.

v4.1.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.0.0...v4.1.0

v4.0.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v3...v4.0.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

v3.1.0

v3.0.2

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3.6.0&new-version=4.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index b187c4637..657b236db 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From 643e1ecbbeb8cd97c5d49ed7b4cc84ec4ece4abb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:12:43 +0000 Subject: [PATCH 098/113] Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (dart-lang/fake_async#69) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.5.1 to 1.6.0.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/fake_async#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/fake_async#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

  • Added a flavor option setup.sh to allow downloading unpublished builds.

v1.0.0

  • Promoted to 1.0 stable.

v0.5

  • Fixed a Windows pub global activate path issue.

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.5.1&new-version=1.6.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 657b236db..86a16617d 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install From fee768f36fad360c752979baf05dd6992b41142b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:55:59 +0000 Subject: [PATCH 099/113] Bump actions/checkout from 4.1.0 to 4.1.1 (dart-lang/fake_async#70) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.0 to 4.1.1.
Release notes

Sourced from actions/checkout's releases.

v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.0...v4.1.1

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.0&new-version=4.1.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 86a16617d..4cb7d3a73 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.0.0, dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} From bdcdba1cf14eb8ca6192c0f971a92ab01f1b2a13 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:44:55 +0000 Subject: [PATCH 100/113] Bump dart-lang/setup-dart from 1.6.0 to 1.6.2 (dart-lang/fake_async#73) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.0 to 1.6.2.
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.
Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

  • Automatically create OIDC token for pub.dev.
  • Add a reusable workflow for publishing.

v1.3.0

  • The install location of the Dart SDK is now available in an environment variable, DART_HOME (dart-lang/fake_async#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/fake_async#35).

v1.2.0

  • Fixed a path issue impacting git dependencies on Windows.

v1.1.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dart-lang/setup-dart&package-manager=github_actions&previous-version=1.6.0&new-version=1.6.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 4cb7d3a73..4e4e97d8d 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.0.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d + - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} - id: install From 2f366fae8b04fda58152cb505d8d703aae53cd88 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 20 Feb 2024 12:54:17 -0800 Subject: [PATCH 101/113] blast_repo fixes (dart-lang/fake_async#76) auto-publish, github-actions, no-response --- .../.github/workflows/no-response.yml | 37 +++++++++++++++++++ .../fake_async/.github/workflows/publish.yaml | 17 +++++++++ 2 files changed, 54 insertions(+) create mode 100644 pkgs/fake_async/.github/workflows/no-response.yml create mode 100644 pkgs/fake_async/.github/workflows/publish.yaml diff --git a/pkgs/fake_async/.github/workflows/no-response.yml b/pkgs/fake_async/.github/workflows/no-response.yml new file mode 100644 index 000000000..ab1ac4984 --- /dev/null +++ b/pkgs/fake_async/.github/workflows/no-response.yml @@ -0,0 +1,37 @@ +# A workflow to close issues where the author hasn't responded to a request for +# more information; see https://github.com/actions/stale. + +name: No Response + +# Run as a daily cron. +on: + schedule: + # Every day at 8am + - cron: '0 8 * * *' + +# All permissions not specified are set to 'none'. +permissions: + issues: write + pull-requests: write + +jobs: + no-response: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'dart-lang' }} + steps: + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e + with: + # Don't automatically mark inactive issues+PRs as stale. + days-before-stale: -1 + # Close needs-info issues and PRs after 14 days of inactivity. + days-before-close: 14 + stale-issue-label: "needs-info" + close-issue-message: > + Without additional information we're not able to resolve this issue. + Feel free to add more info or respond to any questions above and we + can reopen the case. Thanks for your contribution! + stale-pr-label: "needs-info" + close-pr-message: > + Without additional information we're not able to resolve this PR. + Feel free to add more info or respond to any questions above. + Thanks for your contribution! diff --git a/pkgs/fake_async/.github/workflows/publish.yaml b/pkgs/fake_async/.github/workflows/publish.yaml new file mode 100644 index 000000000..27157a046 --- /dev/null +++ b/pkgs/fake_async/.github/workflows/publish.yaml @@ -0,0 +1,17 @@ +# A CI configuration to auto-publish pub packages. + +name: Publish + +on: + pull_request: + branches: [ master ] + push: + tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ] + +jobs: + publish: + if: ${{ github.repository_owner == 'dart-lang' }} + uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main + permissions: + id-token: write # Required for authentication using OIDC + pull-requests: write # Required for writing the pull request note From 120f8edf0ddfc4ee551dcd3f17a1c7e31fd9be7e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 20 Feb 2024 12:54:47 -0800 Subject: [PATCH 102/113] Bump lints, require Dart 3.3, test wasm on dev channel (dart-lang/fake_async#77) --- .../.github/workflows/test-package.yml | 7 ++++-- pkgs/fake_async/CHANGELOG.md | 2 +- pkgs/fake_async/pubspec.yaml | 4 ++-- pkgs/fake_async/test/fake_async_test.dart | 22 +++++++++---------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 4e4e97d8d..7ff414658 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -47,7 +47,7 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [3.0.0, dev] + sdk: [3.3, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 @@ -59,6 +59,9 @@ jobs: - name: Run VM tests run: dart test --platform vm if: always() && steps.install.outcome == 'success' - - name: Run Chrome tests + - name: Run Chrome tests - js run: dart test --platform chrome if: always() && steps.install.outcome == 'success' + - name: Run Chrome tests - wasm + run: dart test --platform chrome --compiler dart2wasm + if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 7e998ab6d..61c8b85e4 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,6 +1,6 @@ ## 1.3.2-wip -* Require Dart 3.0.0 +* Require Dart 3.3 ## 1.3.1 diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 51cc15369..191139f87 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -6,7 +6,7 @@ description: >- repository: https://github.com/dart-lang/fake_async environment: - sdk: ^3.0.0 + sdk: ^3.3.0 dependencies: clock: ^1.1.0 @@ -14,5 +14,5 @@ dependencies: dev_dependencies: async: ^2.5.0 - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 test: ^1.16.0 diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index e0d78bb3c..7aefc5fe8 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -121,7 +121,7 @@ void main() { test('should call timers occurring at the same time in FIFO order', () { FakeAsync().run((async) { - final log = []; + final log = []; Timer(elapseBy ~/ 2, () => log.add('1')); Timer(elapseBy ~/ 2, () => log.add('2')); async.elapse(elapseBy); @@ -131,7 +131,7 @@ void main() { test('should maintain FIFO order even with periodic timers', () { FakeAsync().run((async) { - final log = []; + final log = []; Timer.periodic(elapseBy ~/ 2, (_) => log.add('periodic 1')); Timer(elapseBy ~/ 2, () => log.add('delayed 1')); Timer(elapseBy, () => log.add('delayed 2')); @@ -191,7 +191,7 @@ void main() { test('should add event before advancing time', () { FakeAsync().run((async) { - final controller = StreamController(); + final controller = StreamController(); expect(controller.stream.first.then((_) { expect(async.elapsed, Duration.zero); }), completes); @@ -259,7 +259,7 @@ void main() { test('should work with Future.timeout', () { FakeAsync().run((async) { - final completer = Completer(); + final completer = Completer(); expect(completer.future.timeout(elapseBy ~/ 2), throwsA(const TypeMatcher())); async.elapse(elapseBy); @@ -285,7 +285,7 @@ void main() { final timed = controller.stream.timeout(const Duration(minutes: 2)); final events = []; - final errors = []; + final errors = []; timed.listen(events.add, onError: errors.add); controller.add(0); @@ -310,7 +310,7 @@ void main() { test('should flush microtasks scheduled by microtasks in order', () { FakeAsync().run((async) { - final log = []; + final log = []; scheduleMicrotask(() { log.add(1); scheduleMicrotask(() => log.add(3)); @@ -324,7 +324,7 @@ void main() { test('should not run timers', () { FakeAsync().run((async) { - final log = []; + final log = []; scheduleMicrotask(() => log.add(1)); Timer.run(() => log.add(2)); Timer.periodic(const Duration(seconds: 1), (_) => log.add(2)); @@ -338,7 +338,7 @@ void main() { group('flushTimers', () { test('should flush timers in FIFO order', () { FakeAsync().run((async) { - final log = []; + final log = []; Timer.run(() { log.add(1); Timer(elapseBy, () => log.add(3)); @@ -355,7 +355,7 @@ void main() { 'should run collateral periodic timers with non-periodic first if ' 'scheduled first', () { FakeAsync().run((async) { - final log = []; + final log = []; Timer(const Duration(seconds: 2), () => log.add('delayed')); Timer.periodic(const Duration(seconds: 1), (_) => log.add('periodic')); @@ -368,7 +368,7 @@ void main() { 'should run collateral periodic timers with periodic first ' 'if scheduled first', () { FakeAsync().run((async) { - final log = []; + final log = []; Timer.periodic(const Duration(seconds: 1), (_) => log.add('periodic')); Timer(const Duration(seconds: 2), () => log.add('delayed')); @@ -574,7 +574,7 @@ void main() { test('should increment tick in a periodic timer', () { return FakeAsync().run((async) { - final ticks = []; + final ticks = []; Timer.periodic( elapseBy, expectAsync1((timer) { From 3383729c87003f09a2e074afbbd257ff4fba9a24 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 14:03:05 +0000 Subject: [PATCH 103/113] Bump actions/checkout from 4.1.1 to 4.1.2 (dart-lang/fake_async#78) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.1 to 4.1.2.
Release notes

Sourced from actions/checkout's releases.

v4.1.2

We are investigating the following issue with this release and have rolled-back the v4 tag to point to v4.1.1

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.1...v4.1.2

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

v3.3.0

v3.2.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.1&new-version=4.1.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 7ff414658..a98941fcf 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.3, dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From a77357ee1de6a19febb2903a04ee81be5a441e6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 14:27:06 +0000 Subject: [PATCH 104/113] Bump actions/checkout from 4.1.2 to 4.1.4 (dart-lang/fake_async#81) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.2 to 4.1.4.
Release notes

Sourced from actions/checkout's releases.

v4.1.4

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.3...v4.1.4

v4.1.3

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.2...v4.1.3

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

v3.5.0

v3.4.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.2&new-version=4.1.4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index a98941fcf..6a9ec53eb 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.3, dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From 966a932b44dac75d37201a545c82124ec707447a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 08:14:20 -0700 Subject: [PATCH 105/113] Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (dart-lang/fake_async#80) Bumps [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart) from 1.6.2 to 1.6.4. - [Release notes](https://github.com/dart-lang/setup-dart/releases) - [Changelog](https://github.com/dart-lang/setup-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/dart-lang/setup-dart/compare/fedb1266e91cf51be2fdb382869461a434b920a3...f0ead981b4d9a35b37f30d36160575d60931ec30) --- updated-dependencies: - dependency-name: dart-lang/setup-dart dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 6a9ec53eb..c13971c58 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [3.3, dev] steps: - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 + - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} - id: install From ec283bd1df282b452a5dddd7c4955d12f9d7d0ba Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 6 May 2024 10:05:33 -0700 Subject: [PATCH 106/113] blast_repo fixes (dart-lang/fake_async#82) dependabot --- pkgs/fake_async/.github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/fake_async/.github/dependabot.yml b/pkgs/fake_async/.github/dependabot.yml index 725f03af2..cde02ad6a 100644 --- a/pkgs/fake_async/.github/dependabot.yml +++ b/pkgs/fake_async/.github/dependabot.yml @@ -9,3 +9,7 @@ updates: interval: monthly labels: - autosubmit + groups: + github-actions: + patterns: + - "*" From d2cba5196bfdace6c1f8bfcaba7c27edcdedda85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:16:21 +0000 Subject: [PATCH 107/113] Bump actions/checkout from 4.1.4 to 4.1.6 in the github-actions group (dart-lang/fake_async#83) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.4 to 4.1.6
Release notes

Sourced from actions/checkout's releases.

v4.1.6

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.5...v4.1.6

v4.1.5

What's Changed

Full Changelog: https://github.com/actions/checkout/compare/v4.1.4...v4.1.5

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

v3.5.2

v3.5.1

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.4&new-version=4.1.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index c13971c58..6205a5371 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.3, dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From 9e1284057377402d6f28f618f09474ed72c93014 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:14:56 +0000 Subject: [PATCH 108/113] Bump the github-actions group with 2 updates (dart-lang/fake_async#86) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [dart-lang/setup-dart](https://github.com/dart-lang/setup-dart). Updates `actions/checkout` from 4.1.6 to 4.1.7
Release notes

Sourced from actions/checkout's releases.

v4.1.7

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.6...v4.1.7

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.1.7

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

v3.5.3

... (truncated)

Commits

Updates `dart-lang/setup-dart` from 1.6.4 to 1.6.5
Release notes

Sourced from dart-lang/setup-dart's releases.

v1.6.5

dart-lang/fake_async#118: dart-lang/setup-dartdart-lang/fake_async#118

Changelog

Sourced from dart-lang/setup-dart's changelog.

v1.6.5

dart-lang/fake_async#118: dart-lang/setup-dartdart-lang/fake_async#118

v1.6.4

  • Rebuild JS code.

v1.6.3

v1.6.2

v1.6.1

  • Updated the google storage url for main channel releases.

v1.6.0

  • Enable provisioning of the latest Dart SDK patch release by specifying just the major and minor version (e.g. 3.2).

v1.5.1

  • No longer test the setup-dart action on pre-2.12 SDKs.
  • Upgrade JS interop code to use extension types (the new name for inline classes).
  • The upcoming rename of the be channel to main is now supported with forward compatibility that switches when the rename happens.

v1.5.0

  • Re-wrote the implementation of the action into Dart.
  • Auto-detect the platform architecture (x64, ia32, arm, arm64).
  • Improved the caching and download resilience of the sdk.
  • Added a new action output: dart-version - the installed version of the sdk.

v1.4.0

... (truncated)

Commits

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/fake_async/.github/workflows/test-package.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 6205a5371..09ad7b192 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,8 +22,8 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} - id: install @@ -49,8 +49,8 @@ jobs: os: [ubuntu-latest] sdk: [3.3, dev] steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} - id: install From 4d08590574fe2e86cfc11cb62768e0b89bcf3a7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:29:09 +0000 Subject: [PATCH 109/113] Bump actions/checkout from 4.1.7 to 4.2.0 in the github-actions group (dart-lang/fake_async#87) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 4.1.7 to 4.2.0
Release notes

Sourced from actions/checkout's releases.

v4.2.0

What's Changed

New Contributors

Full Changelog: https://github.com/actions/checkout/compare/v4.1.7...v4.2.0

Changelog

Sourced from actions/checkout's changelog.

Changelog

v4.2.0

v4.1.7

v4.1.6

v4.1.5

v4.1.4

v4.1.3

v4.1.2

v4.1.1

v4.1.0

v4.0.0

v3.6.0

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=4.1.7&new-version=4.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions
--- pkgs/fake_async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml index 09ad7b192..9509196cc 100644 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ b/pkgs/fake_async/.github/workflows/test-package.yml @@ -22,7 +22,7 @@ jobs: matrix: sdk: [dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [3.3, dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} From 26a9831e701e05f4d759be64474058cc3a464fa0 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Fri, 4 Oct 2024 16:04:55 -0700 Subject: [PATCH 110/113] Fully update internal state before invoking periodic-timer callback (dart-lang/fake_async#89) Fixes dart-lang/fake_async#88. Currently when a periodic timer's callback gets invoked, its `_nextCall` is still the time of the current call, not the next one. If the timer callback itself calls `flushTimers` or `elapse`, this causes the same timer to immediately get called again. Fortunately the fix is easy: update `_nextCall` just before invoking `_callback`, instead of just after. --- To work through why this is a complete fix (and doesn't leave further bugs of this kind still to be fixed): After this fix, the call to the timer's callback is a tail call from `FakeTimer._fire`. Because the call site of `FakeTimer._fire` is immediately followed by `flushMicrotasks()`, this means calling other `FakeAsync` methods from the timer callback is no different from doing so in a subsequent microtask. Moreover, when running timers from `flushTimers`, if after the `flushMicrotasks` call this turns out to be the last timer to run, then `flushTimers` will return with no further updates to the state. So when the timer callback is invoked (in that case), the whole `FakeAsync` state must already be in a state that `flushTimers` would have been happy to leave it in. (And there's no special cleanup that it does only after a non-last timer.) Similarly, when running timers from `elapse` (the only other possibility), the only difference from a state that `elapse` would be happy to leave things in is that `_elapsingTo` is still set. That field affects only `elapse` and `elapseBlocking`; and those are both designed to handle being called from within `elapse`. --- pkgs/fake_async/CHANGELOG.md | 3 +++ pkgs/fake_async/lib/fake_async.dart | 2 +- pkgs/fake_async/test/fake_async_test.dart | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 61c8b85e4..5c7dbd06e 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,6 +1,9 @@ ## 1.3.2-wip * Require Dart 3.3 +* Fix bug where a `flushTimers` or `elapse` call from within + the callback of a periodic timer would immediately invoke + the same timer. ## 1.3.1 diff --git a/pkgs/fake_async/lib/fake_async.dart b/pkgs/fake_async/lib/fake_async.dart index 415cb967e..b4eea856b 100644 --- a/pkgs/fake_async/lib/fake_async.dart +++ b/pkgs/fake_async/lib/fake_async.dart @@ -320,9 +320,9 @@ class FakeTimer implements Timer { assert(isActive); _tick++; if (isPeriodic) { + _nextCall += duration; // ignore: avoid_dynamic_calls _callback(this); - _nextCall += duration; } else { cancel(); // ignore: avoid_dynamic_calls diff --git a/pkgs/fake_async/test/fake_async_test.dart b/pkgs/fake_async/test/fake_async_test.dart index 7aefc5fe8..463eecdb8 100644 --- a/pkgs/fake_async/test/fake_async_test.dart +++ b/pkgs/fake_async/test/fake_async_test.dart @@ -586,6 +586,23 @@ void main() { expect(ticks, [1, 2]); }); }); + + test('should update periodic timer state before invoking callback', () { + // Regression test for: https://github.com/dart-lang/fake_async/issues/88 + FakeAsync().run((async) { + final log = []; + Timer.periodic(const Duration(seconds: 2), (timer) { + log.add('periodic ${timer.tick}'); + async.elapse(Duration.zero); + }); + Timer(const Duration(seconds: 3), () { + log.add('single'); + }); + + async.flushTimers(flushPeriodicTimers: false); + expect(log, ['periodic 1', 'single']); + }); + }); }); group('clock', () { From 1dfd511ff749a9e9362a2c20c939575524afe908 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 16 Oct 2024 09:48:10 +0200 Subject: [PATCH 111/113] Add issue template and other fixes --- .github/ISSUE_TEMPLATE/fake_async.md | 5 +++++ pkgs/fake_async/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/fake_async.md diff --git a/.github/ISSUE_TEMPLATE/fake_async.md b/.github/ISSUE_TEMPLATE/fake_async.md new file mode 100644 index 000000000..c26edd2ec --- /dev/null +++ b/.github/ISSUE_TEMPLATE/fake_async.md @@ -0,0 +1,5 @@ +--- +name: "package:fake_async" +about: "Create a bug or file a feature request against package:fake_async." +labels: "package:fake_async" +--- \ No newline at end of file diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index 191139f87..d43dcd579 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -3,7 +3,7 @@ version: 1.3.2-wip description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. -repository: https://github.com/dart-lang/fake_async +repository: https://github.com/dart-lang/test/tree/main/pkgs/fake_async environment: sdk: ^3.3.0 From 057fafd3f5b45608b717538d91ff95f2629d347d Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 16 Oct 2024 09:57:57 +0200 Subject: [PATCH 112/113] Moving fixes --- .github/workflows/dart.yml | 326 +++++++++++++----- README.md | 1 + pkgs/fake_async/.github/dependabot.yml | 15 - .../.github/workflows/no-response.yml | 37 -- .../fake_async/.github/workflows/publish.yaml | 17 - .../.github/workflows/test-package.yml | 67 ---- pkgs/fake_async/CHANGELOG.md | 4 +- pkgs/fake_async/CONTRIBUTING.md | 51 --- pkgs/fake_async/README.md | 1 - pkgs/fake_async/mono_pkg.yaml | 15 + pkgs/fake_async/pubspec.yaml | 9 +- pubspec.yaml | 1 + 12 files changed, 267 insertions(+), 277 deletions(-) delete mode 100644 pkgs/fake_async/.github/dependabot.yml delete mode 100644 pkgs/fake_async/.github/workflows/no-response.yml delete mode 100644 pkgs/fake_async/.github/workflows/publish.yaml delete mode 100644 pkgs/fake_async/.github/workflows/test-package.yml delete mode 100644 pkgs/fake_async/CONTRIBUTING.md create mode 100644 pkgs/fake_async/mono_pkg.yaml diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index e1b92f73a..33ea69484 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -34,7 +34,7 @@ jobs: sdk: stable - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - name: mono_repo self validate run: dart pub global activate mono_repo 6.6.2 - name: mono_repo self validate @@ -59,7 +59,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_regression_pub_upgrade name: integration_tests/regression; dart pub upgrade run: dart pub upgrade @@ -106,7 +106,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_checks_pub_upgrade name: pkgs/checks; dart pub upgrade run: dart pub upgrade @@ -126,16 +126,46 @@ jobs: if: "always() && steps.pkgs_test_core_pub_upgrade.conclusion == 'success'" working-directory: pkgs/test_core job_004: - name: "analyze_and_format; linux; Dart dev; PKGS: integration_tests/regression, integration_tests/spawn_hybrid, integration_tests/wasm, pkgs/checks, pkgs/test, pkgs/test_api, pkgs/test_core; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`" + name: "analyze_and_format; linux; Dart 3.5.0; PKG: pkgs/fake_async; `dart analyze`" runs-on: ubuntu-latest steps: - name: Cache Pub hosted dependencies uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 with: path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:integration_tests/regression-integration_tests/spawn_hybrid-integration_tests/wasm-pkgs/checks-pkgs/test-pkgs/test_api-pkgs/test_core;commands:format-analyze_0" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async;commands:analyze_1" restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:integration_tests/regression-integration_tests/spawn_hybrid-integration_tests/wasm-pkgs/checks-pkgs/test-pkgs/test_api-pkgs/test_core + os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async + os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: "3.5.0" + - id: checkout + name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - id: pkgs_fake_async_pub_upgrade + name: pkgs/fake_async; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: pkgs/fake_async; dart analyze + run: dart analyze + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async + job_005: + name: "analyze_and_format; linux; Dart dev; PKGS: integration_tests/regression, integration_tests/spawn_hybrid, integration_tests/wasm, pkgs/checks, pkgs/fake_async, pkgs/test, pkgs/test_api, pkgs/test_core; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:integration_tests/regression-integration_tests/spawn_hybrid-integration_tests/wasm-pkgs/checks-pkgs/fake_async-pkgs/test-pkgs/test_api-pkgs/test_core;commands:format-analyze_0" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:integration_tests/regression-integration_tests/spawn_hybrid-integration_tests/wasm-pkgs/checks-pkgs/fake_async-pkgs/test-pkgs/test_api-pkgs/test_core os:ubuntu-latest;pub-cache-hosted;sdk:dev os:ubuntu-latest;pub-cache-hosted os:ubuntu-latest @@ -145,7 +175,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_regression_pub_upgrade name: integration_tests/regression; dart pub upgrade run: dart pub upgrade @@ -198,6 +228,19 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_checks_pub_upgrade.conclusion == 'success'" working-directory: pkgs/checks + - id: pkgs_fake_async_pub_upgrade + name: pkgs/fake_async; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: "pkgs/fake_async; dart format --output=none --set-exit-if-changed ." + run: "dart format --output=none --set-exit-if-changed ." + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: "pkgs/fake_async; dart analyze --fatal-infos" + run: dart analyze --fatal-infos + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -237,7 +280,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.pkgs_test_core_pub_upgrade.conclusion == 'success'" working-directory: pkgs/test_core - job_005: + job_006: name: "analyze_and_format; windows; Dart 3.5.0-311.0.dev; PKG: integration_tests/wasm; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`" runs-on: windows-latest steps: @@ -247,7 +290,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_wasm_pub_upgrade name: integration_tests/wasm; dart pub upgrade run: dart pub upgrade @@ -261,7 +304,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'" working-directory: integration_tests/wasm - job_006: + job_007: name: "analyze_and_format; windows; Dart dev; PKG: integration_tests/wasm; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`" runs-on: windows-latest steps: @@ -271,7 +314,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_wasm_pub_upgrade name: integration_tests/wasm; dart pub upgrade run: dart pub upgrade @@ -285,7 +328,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'" working-directory: integration_tests/wasm - job_007: + job_008: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: integration_tests/regression; `dart test`" runs-on: ubuntu-latest steps: @@ -305,7 +348,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_regression_pub_upgrade name: integration_tests/regression; dart pub upgrade run: dart pub upgrade @@ -322,7 +365,8 @@ jobs: - job_004 - job_005 - job_006 - job_008: + - job_007 + job_009: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/checks; `dart test`" runs-on: ubuntu-latest steps: @@ -342,7 +386,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_checks_pub_upgrade name: pkgs/checks; dart pub upgrade run: dart pub upgrade @@ -359,7 +403,8 @@ jobs: - job_004 - job_005 - job_006 - job_009: + - job_007 + job_010: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test_core; `dart test`" runs-on: ubuntu-latest steps: @@ -379,7 +424,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_core_pub_upgrade name: pkgs/test_core; dart pub upgrade run: dart pub upgrade @@ -396,7 +441,8 @@ jobs: - job_004 - job_005 - job_006 - job_010: + - job_007 + job_011: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`" runs-on: ubuntu-latest steps: @@ -416,7 +462,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_spawn_hybrid_pub_upgrade name: integration_tests/spawn_hybrid; dart pub upgrade run: dart pub upgrade @@ -433,7 +479,8 @@ jobs: - job_004 - job_005 - job_006 - job_011: + - job_007 + job_012: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: integration_tests/wasm; `dart test --timeout=60s`" runs-on: ubuntu-latest steps: @@ -453,7 +500,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_wasm_pub_upgrade name: integration_tests/wasm; dart pub upgrade run: dart pub upgrade @@ -470,7 +517,8 @@ jobs: - job_004 - job_005 - job_006 - job_012: + - job_007 + job_013: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 0`" runs-on: ubuntu-latest steps: @@ -490,7 +538,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -507,7 +555,8 @@ jobs: - job_004 - job_005 - job_006 - job_013: + - job_007 + job_014: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 1`" runs-on: ubuntu-latest steps: @@ -527,7 +576,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -544,7 +593,8 @@ jobs: - job_004 - job_005 - job_006 - job_014: + - job_007 + job_015: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 2`" runs-on: ubuntu-latest steps: @@ -564,7 +614,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -581,7 +631,8 @@ jobs: - job_004 - job_005 - job_006 - job_015: + - job_007 + job_016: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 3`" runs-on: ubuntu-latest steps: @@ -601,7 +652,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -618,7 +669,8 @@ jobs: - job_004 - job_005 - job_006 - job_016: + - job_007 + job_017: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 4`" runs-on: ubuntu-latest steps: @@ -638,7 +690,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -655,7 +707,8 @@ jobs: - job_004 - job_005 - job_006 - job_017: + - job_007 + job_018: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test_api; `dart test --preset travis -x browser`" runs-on: ubuntu-latest steps: @@ -675,7 +728,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_api_pub_upgrade name: pkgs/test_api; dart pub upgrade run: dart pub upgrade @@ -692,7 +745,46 @@ jobs: - job_004 - job_005 - job_006 - job_018: + - job_007 + job_019: + name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/fake_async; `dart test`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async;commands:command_00" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async + os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: "3.5.0" + - id: checkout + name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - id: pkgs_fake_async_pub_upgrade + name: pkgs/fake_async; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: pkgs/fake_async; dart test + run: dart test + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async + needs: + - job_001 + - job_002 + - job_003 + - job_004 + - job_005 + - job_006 + - job_007 + job_020: name: "unit_test; linux; Dart dev; PKG: integration_tests/regression; `dart test`" runs-on: ubuntu-latest steps: @@ -712,7 +804,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_regression_pub_upgrade name: integration_tests/regression; dart pub upgrade run: dart pub upgrade @@ -729,7 +821,8 @@ jobs: - job_004 - job_005 - job_006 - job_019: + - job_007 + job_021: name: "unit_test; linux; Dart dev; PKG: pkgs/checks; `dart test`" runs-on: ubuntu-latest steps: @@ -749,7 +842,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_checks_pub_upgrade name: pkgs/checks; dart pub upgrade run: dart pub upgrade @@ -766,7 +859,46 @@ jobs: - job_004 - job_005 - job_006 - job_020: + - job_007 + job_022: + name: "unit_test; linux; Dart dev; PKG: pkgs/fake_async; `dart test`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/fake_async;commands:command_00" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:dev;packages:pkgs/fake_async + os:ubuntu-latest;pub-cache-hosted;sdk:dev + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: dev + - id: checkout + name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - id: pkgs_fake_async_pub_upgrade + name: pkgs/fake_async; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: pkgs/fake_async; dart test + run: dart test + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async + needs: + - job_001 + - job_002 + - job_003 + - job_004 + - job_005 + - job_006 + - job_007 + job_023: name: "unit_test; linux; Dart dev; PKG: pkgs/test_core; `dart test`" runs-on: ubuntu-latest steps: @@ -786,7 +918,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_core_pub_upgrade name: pkgs/test_core; dart pub upgrade run: dart pub upgrade @@ -803,7 +935,8 @@ jobs: - job_004 - job_005 - job_006 - job_021: + - job_007 + job_024: name: "unit_test; linux; Dart dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`" runs-on: ubuntu-latest steps: @@ -823,7 +956,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_spawn_hybrid_pub_upgrade name: integration_tests/spawn_hybrid; dart pub upgrade run: dart pub upgrade @@ -840,7 +973,8 @@ jobs: - job_004 - job_005 - job_006 - job_022: + - job_007 + job_025: name: "unit_test; linux; Dart dev; PKG: integration_tests/wasm; `dart test --timeout=60s`" runs-on: ubuntu-latest steps: @@ -860,7 +994,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_wasm_pub_upgrade name: integration_tests/wasm; dart pub upgrade run: dart pub upgrade @@ -877,7 +1011,8 @@ jobs: - job_004 - job_005 - job_006 - job_023: + - job_007 + job_026: name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 0`" runs-on: ubuntu-latest steps: @@ -897,7 +1032,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -914,7 +1049,8 @@ jobs: - job_004 - job_005 - job_006 - job_024: + - job_007 + job_027: name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 1`" runs-on: ubuntu-latest steps: @@ -934,7 +1070,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -951,7 +1087,8 @@ jobs: - job_004 - job_005 - job_006 - job_025: + - job_007 + job_028: name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 2`" runs-on: ubuntu-latest steps: @@ -971,7 +1108,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -988,7 +1125,8 @@ jobs: - job_004 - job_005 - job_006 - job_026: + - job_007 + job_029: name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 3`" runs-on: ubuntu-latest steps: @@ -1008,7 +1146,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1025,7 +1163,8 @@ jobs: - job_004 - job_005 - job_006 - job_027: + - job_007 + job_030: name: "unit_test; linux; Dart dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 4`" runs-on: ubuntu-latest steps: @@ -1045,7 +1184,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1062,7 +1201,8 @@ jobs: - job_004 - job_005 - job_006 - job_028: + - job_007 + job_031: name: "unit_test; linux; Dart dev; PKG: pkgs/test_api; `dart test --preset travis -x browser`" runs-on: ubuntu-latest steps: @@ -1082,7 +1222,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_api_pub_upgrade name: pkgs/test_api; dart pub upgrade run: dart pub upgrade @@ -1099,7 +1239,8 @@ jobs: - job_004 - job_005 - job_006 - job_029: + - job_007 + job_032: name: "unit_test; osx; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 0`" runs-on: macos-latest steps: @@ -1119,7 +1260,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1136,7 +1277,8 @@ jobs: - job_004 - job_005 - job_006 - job_030: + - job_007 + job_033: name: "unit_test; osx; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 1`" runs-on: macos-latest steps: @@ -1156,7 +1298,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1173,7 +1315,8 @@ jobs: - job_004 - job_005 - job_006 - job_031: + - job_007 + job_034: name: "unit_test; osx; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 2`" runs-on: macos-latest steps: @@ -1193,7 +1336,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1210,7 +1353,8 @@ jobs: - job_004 - job_005 - job_006 - job_032: + - job_007 + job_035: name: "unit_test; osx; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 3`" runs-on: macos-latest steps: @@ -1230,7 +1374,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1247,7 +1391,8 @@ jobs: - job_004 - job_005 - job_006 - job_033: + - job_007 + job_036: name: "unit_test; osx; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 4`" runs-on: macos-latest steps: @@ -1267,7 +1412,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1284,7 +1429,8 @@ jobs: - job_004 - job_005 - job_006 - job_034: + - job_007 + job_037: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`" runs-on: windows-latest steps: @@ -1294,7 +1440,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_spawn_hybrid_pub_upgrade name: integration_tests/spawn_hybrid; dart pub upgrade run: dart pub upgrade @@ -1311,7 +1457,8 @@ jobs: - job_004 - job_005 - job_006 - job_035: + - job_007 + job_038: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: integration_tests/wasm; `dart test --timeout=60s`" runs-on: windows-latest steps: @@ -1321,7 +1468,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_wasm_pub_upgrade name: integration_tests/wasm; dart pub upgrade run: dart pub upgrade @@ -1338,7 +1485,8 @@ jobs: - job_004 - job_005 - job_006 - job_036: + - job_007 + job_039: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 0`" runs-on: windows-latest steps: @@ -1348,7 +1496,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1365,7 +1513,8 @@ jobs: - job_004 - job_005 - job_006 - job_037: + - job_007 + job_040: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 1`" runs-on: windows-latest steps: @@ -1375,7 +1524,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1392,7 +1541,8 @@ jobs: - job_004 - job_005 - job_006 - job_038: + - job_007 + job_041: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 2`" runs-on: windows-latest steps: @@ -1402,7 +1552,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1419,7 +1569,8 @@ jobs: - job_004 - job_005 - job_006 - job_039: + - job_007 + job_042: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 3`" runs-on: windows-latest steps: @@ -1429,7 +1580,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1446,7 +1597,8 @@ jobs: - job_004 - job_005 - job_006 - job_040: + - job_007 + job_043: name: "unit_test; windows; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `dart test --preset travis --total-shards 5 --shard-index 4`" runs-on: windows-latest steps: @@ -1456,7 +1608,7 @@ jobs: sdk: "3.5.0-311.0.dev" - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: pkgs_test_pub_upgrade name: pkgs/test; dart pub upgrade run: dart pub upgrade @@ -1473,7 +1625,8 @@ jobs: - job_004 - job_005 - job_006 - job_041: + - job_007 + job_044: name: "unit_test; windows; Dart dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`" runs-on: windows-latest steps: @@ -1483,7 +1636,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_spawn_hybrid_pub_upgrade name: integration_tests/spawn_hybrid; dart pub upgrade run: dart pub upgrade @@ -1500,7 +1653,8 @@ jobs: - job_004 - job_005 - job_006 - job_042: + - job_007 + job_045: name: "unit_test; windows; Dart dev; PKG: integration_tests/wasm; `dart test --timeout=60s`" runs-on: windows-latest steps: @@ -1510,7 +1664,7 @@ jobs: sdk: dev - id: checkout name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - id: integration_tests_wasm_pub_upgrade name: integration_tests/wasm; dart pub upgrade run: dart pub upgrade @@ -1527,7 +1681,8 @@ jobs: - job_004 - job_005 - job_006 - job_043: + - job_007 + job_046: name: Notify failure runs-on: ubuntu-latest if: "(github.event_name == 'push' || github.event_name == 'schedule') && failure()" @@ -1581,3 +1736,6 @@ jobs: - job_040 - job_041 - job_042 + - job_043 + - job_044 + - job_045 diff --git a/README.md b/README.md index 7091bd97c..df1413b87 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ literate API. | Package | Description | Version | |---|---|---| | [checks](pkgs/checks/) | A framework for checking values against expectations and building custom expectations. | [![pub package](https://img.shields.io/pub/v/checks.svg)](https://pub.dev/packages/checks) | +| [fake_async](pkgs/fake_async/) | Fake asynchronous events such as timers and microtasks for deterministic testing. | [![pub package](https://img.shields.io/pub/v/fake_async.svg)](https://pub.dev/packages/fake_async) | | [test](pkgs/test/) | A full featured library for writing and running Dart tests across platforms. | [![pub package](https://img.shields.io/pub/v/test.svg)](https://pub.dev/packages/test) | | [test_api](pkgs/test_api/) | | [![pub package](https://img.shields.io/pub/v/test_api.svg)](https://pub.dev/packages/test_api) | | [test_core](pkgs/test_core/) | | [![pub package](https://img.shields.io/pub/v/test_core.svg)](https://pub.dev/packages/test_core) | diff --git a/pkgs/fake_async/.github/dependabot.yml b/pkgs/fake_async/.github/dependabot.yml deleted file mode 100644 index cde02ad6a..000000000 --- a/pkgs/fake_async/.github/dependabot.yml +++ /dev/null @@ -1,15 +0,0 @@ -# Dependabot configuration file. -# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates -version: 2 - -updates: - - package-ecosystem: github-actions - directory: / - schedule: - interval: monthly - labels: - - autosubmit - groups: - github-actions: - patterns: - - "*" diff --git a/pkgs/fake_async/.github/workflows/no-response.yml b/pkgs/fake_async/.github/workflows/no-response.yml deleted file mode 100644 index ab1ac4984..000000000 --- a/pkgs/fake_async/.github/workflows/no-response.yml +++ /dev/null @@ -1,37 +0,0 @@ -# A workflow to close issues where the author hasn't responded to a request for -# more information; see https://github.com/actions/stale. - -name: No Response - -# Run as a daily cron. -on: - schedule: - # Every day at 8am - - cron: '0 8 * * *' - -# All permissions not specified are set to 'none'. -permissions: - issues: write - pull-requests: write - -jobs: - no-response: - runs-on: ubuntu-latest - if: ${{ github.repository_owner == 'dart-lang' }} - steps: - - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e - with: - # Don't automatically mark inactive issues+PRs as stale. - days-before-stale: -1 - # Close needs-info issues and PRs after 14 days of inactivity. - days-before-close: 14 - stale-issue-label: "needs-info" - close-issue-message: > - Without additional information we're not able to resolve this issue. - Feel free to add more info or respond to any questions above and we - can reopen the case. Thanks for your contribution! - stale-pr-label: "needs-info" - close-pr-message: > - Without additional information we're not able to resolve this PR. - Feel free to add more info or respond to any questions above. - Thanks for your contribution! diff --git a/pkgs/fake_async/.github/workflows/publish.yaml b/pkgs/fake_async/.github/workflows/publish.yaml deleted file mode 100644 index 27157a046..000000000 --- a/pkgs/fake_async/.github/workflows/publish.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# A CI configuration to auto-publish pub packages. - -name: Publish - -on: - pull_request: - branches: [ master ] - push: - tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ] - -jobs: - publish: - if: ${{ github.repository_owner == 'dart-lang' }} - uses: dart-lang/ecosystem/.github/workflows/publish.yaml@main - permissions: - id-token: write # Required for authentication using OIDC - pull-requests: write # Required for writing the pull request note diff --git a/pkgs/fake_async/.github/workflows/test-package.yml b/pkgs/fake_async/.github/workflows/test-package.yml deleted file mode 100644 index 9509196cc..000000000 --- a/pkgs/fake_async/.github/workflows/test-package.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Dart CI - -on: - # Run on PRs and pushes to the default branch. - push: - branches: [ master ] - pull_request: - branches: [ master ] - schedule: - - cron: "0 0 * * 0" - -env: - PUB_ENVIRONMENT: bot.github - -jobs: - # Check code formatting and static analysis on a single OS (linux) - # against Dart dev. - analyze: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - sdk: [dev] - steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 - with: - sdk: ${{ matrix.sdk }} - - id: install - name: Install dependencies - run: dart pub get - - name: Check formatting - run: dart format --output=none --set-exit-if-changed . - if: always() && steps.install.outcome == 'success' - - name: Analyze code - run: dart analyze --fatal-infos - if: always() && steps.install.outcome == 'success' - - # Run tests on a matrix consisting of two dimensions: - # 1. OS: ubuntu-latest, (macos-latest, windows-latest) - # 2. release channel: dev - test: - needs: analyze - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - # Add macos-latest and/or windows-latest if relevant for this package. - os: [ubuntu-latest] - sdk: [3.3, dev] - steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 - with: - sdk: ${{ matrix.sdk }} - - id: install - name: Install dependencies - run: dart pub get - - name: Run VM tests - run: dart test --platform vm - if: always() && steps.install.outcome == 'success' - - name: Run Chrome tests - js - run: dart test --platform chrome - if: always() && steps.install.outcome == 'success' - - name: Run Chrome tests - wasm - run: dart test --platform chrome --compiler dart2wasm - if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' diff --git a/pkgs/fake_async/CHANGELOG.md b/pkgs/fake_async/CHANGELOG.md index 5c7dbd06e..0f964eede 100644 --- a/pkgs/fake_async/CHANGELOG.md +++ b/pkgs/fake_async/CHANGELOG.md @@ -1,9 +1,11 @@ -## 1.3.2-wip +## 1.3.2 * Require Dart 3.3 * Fix bug where a `flushTimers` or `elapse` call from within the callback of a periodic timer would immediately invoke the same timer. +* Move to `dart-lang/test` monorepo. +* Require Dart 3.5. ## 1.3.1 diff --git a/pkgs/fake_async/CONTRIBUTING.md b/pkgs/fake_async/CONTRIBUTING.md deleted file mode 100644 index 2e76c7cbc..000000000 --- a/pkgs/fake_async/CONTRIBUTING.md +++ /dev/null @@ -1,51 +0,0 @@ -Want to contribute? Great! First, read this page (including the small print at -the end). - -### Before you contribute - -Before we can use your code, you must sign the -[Google Individual Contributor License Agreement][CLA] (CLA), which you can do -online. The CLA is necessary mainly because you own the copyright to your -changes, even after your contribution becomes part of our codebase, so we need -your permission to use and distribute your code. We also need to be sure of -various other things—for instance that you'll tell us if you know that your code -infringes on other people's patents. You don't have to sign the CLA until after -you've submitted your code for review and a member has approved it, but you must -do it before we can put your code into our codebase. - -Before you start working on a larger contribution, you should get in touch with -us first through the issue tracker with your idea so that we can help out and -possibly guide you. Coordinating up front makes it much easier to avoid -frustration later on. - -[CLA]: https://cla.developers.google.com/about/google-individual - -### Code reviews - -All submissions, including submissions by project members, require review. We -recommend [forking the repository][fork], making changes in your fork, and -[sending us a pull request][pr] so we can review the changes and merge them into -this repository. - -[fork]: https://help.github.com/articles/about-forks/ -[pr]: https://help.github.com/articles/creating-a-pull-request/ - -Functional changes will require tests to be added or changed. The tests live in -the `test/` directory, and are run with `pub run test`. If you need to create -new tests, use the existing tests as a guideline for what they should look like. - -Before you send your pull request, make sure all the tests pass! - -### File headers - -When creating new source files, please copy the file header from -https://github.com/dart-lang/fake_async/blob/master/lib/fake_async.dart and -update the year for the copyright to the current year. - -### The small print - -Contributions made by corporations are covered by a different agreement than the -one above, the -[Software Grant and Corporate Contributor License Agreement][CCLA]. - -[CCLA]: https://developers.google.com/open-source/cla/corporate diff --git a/pkgs/fake_async/README.md b/pkgs/fake_async/README.md index c24148248..c7bb40bf4 100644 --- a/pkgs/fake_async/README.md +++ b/pkgs/fake_async/README.md @@ -1,4 +1,3 @@ -[![Dart CI](https://github.com/dart-lang/fake_async/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/fake_async/actions/workflows/test-package.yml) [![pub package](https://img.shields.io/pub/v/fake_async.svg)](https://pub.dev/packages/fake_async) [![package publisher](https://img.shields.io/pub/publisher/fake_async.svg)](https://pub.dev/packages/fake_async/publisher) diff --git a/pkgs/fake_async/mono_pkg.yaml b/pkgs/fake_async/mono_pkg.yaml new file mode 100644 index 000000000..b447edbf8 --- /dev/null +++ b/pkgs/fake_async/mono_pkg.yaml @@ -0,0 +1,15 @@ +# See https://pub.dev/packages/mono_repo + +stages: +- analyze_and_format: + - group: + - format + - analyze: --fatal-infos + sdk: dev + - group: + - analyze + sdk: pubspec +- unit_test: + - group: + - command: dart test + sdk: [dev, pubspec] diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index d43dcd579..f681f12c3 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -1,12 +1,13 @@ name: fake_async -version: 1.3.2-wip +version: 1.3.2 description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. -repository: https://github.com/dart-lang/test/tree/main/pkgs/fake_async +repository: https://github.com/dart-lang/test/tree/master/pkgs/fake_async +resolution: workspace environment: - sdk: ^3.3.0 + sdk: ^3.5.0 dependencies: clock: ^1.1.0 @@ -14,5 +15,5 @@ dependencies: dev_dependencies: async: ^2.5.0 - dart_flutter_team_lints: ^2.0.0 + dart_flutter_team_lints: ^3.1.0 test: ^1.16.0 diff --git a/pubspec.yaml b/pubspec.yaml index 88154f075..4925a12bf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,6 +7,7 @@ workspace: - integration_tests/spawn_hybrid - integration_tests/wasm - pkgs/checks + - pkgs/fake_async - pkgs/test - pkgs/test_api - pkgs/test_core From 01e8e92f3bbe0e06bdd06dbbeacd3d6b750b0070 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 16 Oct 2024 16:53:20 +0200 Subject: [PATCH 113/113] Do not use pub workspace for fake_async --- .github/workflows/dart.yml | 158 +++++++++++++++++------------------ pkgs/fake_async/pubspec.yaml | 6 +- pubspec.yaml | 1 - 3 files changed, 81 insertions(+), 84 deletions(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 33ea69484..6f76daaf7 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -40,6 +40,36 @@ jobs: - name: mono_repo self validate run: dart pub global run mono_repo generate --validate job_002: + name: "analyze_and_format; linux; Dart 3.3.0; PKG: pkgs/fake_async; `dart analyze`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/fake_async;commands:analyze_1" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/fake_async + os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: "3.3.0" + - id: checkout + name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - id: pkgs_fake_async_pub_upgrade + name: pkgs/fake_async; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: pkgs/fake_async; dart analyze + run: dart analyze + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async + job_003: name: "analyze_and_format; linux; Dart 3.5.0-311.0.dev; PKGS: integration_tests/regression, integration_tests/wasm; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`" runs-on: ubuntu-latest steps: @@ -86,7 +116,7 @@ jobs: run: dart analyze --fatal-infos if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'" working-directory: integration_tests/wasm - job_003: + job_004: name: "analyze_and_format; linux; Dart 3.5.0-311.0.dev; PKGS: pkgs/checks, pkgs/test_core; `dart analyze`" runs-on: ubuntu-latest steps: @@ -125,36 +155,6 @@ jobs: run: dart analyze if: "always() && steps.pkgs_test_core_pub_upgrade.conclusion == 'success'" working-directory: pkgs/test_core - job_004: - name: "analyze_and_format; linux; Dart 3.5.0; PKG: pkgs/fake_async; `dart analyze`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async;commands:analyze_1" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async - os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - name: Setup Dart SDK - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 - with: - sdk: "3.5.0" - - id: checkout - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - id: pkgs_fake_async_pub_upgrade - name: pkgs/fake_async; dart pub upgrade - run: dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/fake_async - - name: pkgs/fake_async; dart analyze - run: dart analyze - if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/fake_async job_005: name: "analyze_and_format; linux; Dart dev; PKGS: integration_tests/regression, integration_tests/spawn_hybrid, integration_tests/wasm, pkgs/checks, pkgs/fake_async, pkgs/test, pkgs/test_api, pkgs/test_core; `dart format --output=none --set-exit-if-changed .`, `dart analyze --fatal-infos`" runs-on: ubuntu-latest @@ -329,6 +329,44 @@ jobs: if: "always() && steps.integration_tests_wasm_pub_upgrade.conclusion == 'success'" working-directory: integration_tests/wasm job_008: + name: "unit_test; linux; Dart 3.3.0; PKG: pkgs/fake_async; `dart test`" + runs-on: ubuntu-latest + steps: + - name: Cache Pub hosted dependencies + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 + with: + path: "~/.pub-cache/hosted" + key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/fake_async;commands:command_00" + restore-keys: | + os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0;packages:pkgs/fake_async + os:ubuntu-latest;pub-cache-hosted;sdk:3.3.0 + os:ubuntu-latest;pub-cache-hosted + os:ubuntu-latest + - name: Setup Dart SDK + uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 + with: + sdk: "3.3.0" + - id: checkout + name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - id: pkgs_fake_async_pub_upgrade + name: pkgs/fake_async; dart pub upgrade + run: dart pub upgrade + if: "always() && steps.checkout.conclusion == 'success'" + working-directory: pkgs/fake_async + - name: pkgs/fake_async; dart test + run: dart test + if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" + working-directory: pkgs/fake_async + needs: + - job_001 + - job_002 + - job_003 + - job_004 + - job_005 + - job_006 + - job_007 + job_009: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: integration_tests/regression; `dart test`" runs-on: ubuntu-latest steps: @@ -366,7 +404,7 @@ jobs: - job_005 - job_006 - job_007 - job_009: + job_010: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/checks; `dart test`" runs-on: ubuntu-latest steps: @@ -404,7 +442,7 @@ jobs: - job_005 - job_006 - job_007 - job_010: + job_011: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test_core; `dart test`" runs-on: ubuntu-latest steps: @@ -442,7 +480,7 @@ jobs: - job_005 - job_006 - job_007 - job_011: + job_012: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: integration_tests/spawn_hybrid; `dart test -p chrome,vm,node`" runs-on: ubuntu-latest steps: @@ -480,7 +518,7 @@ jobs: - job_005 - job_006 - job_007 - job_012: + job_013: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: integration_tests/wasm; `dart test --timeout=60s`" runs-on: ubuntu-latest steps: @@ -518,7 +556,7 @@ jobs: - job_005 - job_006 - job_007 - job_013: + job_014: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 0`" runs-on: ubuntu-latest steps: @@ -556,7 +594,7 @@ jobs: - job_005 - job_006 - job_007 - job_014: + job_015: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 1`" runs-on: ubuntu-latest steps: @@ -594,7 +632,7 @@ jobs: - job_005 - job_006 - job_007 - job_015: + job_016: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 2`" runs-on: ubuntu-latest steps: @@ -632,7 +670,7 @@ jobs: - job_005 - job_006 - job_007 - job_016: + job_017: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 3`" runs-on: ubuntu-latest steps: @@ -670,7 +708,7 @@ jobs: - job_005 - job_006 - job_007 - job_017: + job_018: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test; `xvfb-run -s \"-screen 0 1024x768x24\" dart test --preset travis --total-shards 5 --shard-index 4`" runs-on: ubuntu-latest steps: @@ -708,7 +746,7 @@ jobs: - job_005 - job_006 - job_007 - job_018: + job_019: name: "unit_test; linux; Dart 3.5.0-311.0.dev; PKG: pkgs/test_api; `dart test --preset travis -x browser`" runs-on: ubuntu-latest steps: @@ -746,44 +784,6 @@ jobs: - job_005 - job_006 - job_007 - job_019: - name: "unit_test; linux; Dart 3.5.0; PKG: pkgs/fake_async; `dart test`" - runs-on: ubuntu-latest - steps: - - name: Cache Pub hosted dependencies - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 - with: - path: "~/.pub-cache/hosted" - key: "os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async;commands:command_00" - restore-keys: | - os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0;packages:pkgs/fake_async - os:ubuntu-latest;pub-cache-hosted;sdk:3.5.0 - os:ubuntu-latest;pub-cache-hosted - os:ubuntu-latest - - name: Setup Dart SDK - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 - with: - sdk: "3.5.0" - - id: checkout - name: Checkout repository - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - - id: pkgs_fake_async_pub_upgrade - name: pkgs/fake_async; dart pub upgrade - run: dart pub upgrade - if: "always() && steps.checkout.conclusion == 'success'" - working-directory: pkgs/fake_async - - name: pkgs/fake_async; dart test - run: dart test - if: "always() && steps.pkgs_fake_async_pub_upgrade.conclusion == 'success'" - working-directory: pkgs/fake_async - needs: - - job_001 - - job_002 - - job_003 - - job_004 - - job_005 - - job_006 - - job_007 job_020: name: "unit_test; linux; Dart dev; PKG: integration_tests/regression; `dart test`" runs-on: ubuntu-latest diff --git a/pkgs/fake_async/pubspec.yaml b/pkgs/fake_async/pubspec.yaml index f681f12c3..f345860ba 100644 --- a/pkgs/fake_async/pubspec.yaml +++ b/pkgs/fake_async/pubspec.yaml @@ -4,10 +4,8 @@ description: >- Fake asynchronous events such as timers and microtasks for deterministic testing. repository: https://github.com/dart-lang/test/tree/master/pkgs/fake_async -resolution: workspace - environment: - sdk: ^3.5.0 + sdk: ^3.3.0 dependencies: clock: ^1.1.0 @@ -15,5 +13,5 @@ dependencies: dev_dependencies: async: ^2.5.0 - dart_flutter_team_lints: ^3.1.0 + dart_flutter_team_lints: ^2.0.0 test: ^1.16.0 diff --git a/pubspec.yaml b/pubspec.yaml index 4925a12bf..88154f075 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,6 @@ workspace: - integration_tests/spawn_hybrid - integration_tests/wasm - pkgs/checks - - pkgs/fake_async - pkgs/test - pkgs/test_api - pkgs/test_core