From 816d4af72230db509d16c86c620af828aab98782 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 19 Dec 2013 11:57:56 +0000 Subject: [PATCH 001/260] Create associated packages for the dart:collection and dart:async libs. R=sgjesse@google.com Review URL: https://codereview.chromium.org//113883002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@31260 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/README.md | 11 +++++++++++ pkgs/async/lib/async.dart | 7 +++++++ pkgs/async/pubspec.yaml | 9 +++++++++ 3 files changed, 27 insertions(+) create mode 100644 pkgs/async/README.md create mode 100644 pkgs/async/lib/async.dart create mode 100644 pkgs/async/pubspec.yaml diff --git a/pkgs/async/README.md b/pkgs/async/README.md new file mode 100644 index 00000000..6ca48752 --- /dev/null +++ b/pkgs/async/README.md @@ -0,0 +1,11 @@ +The `async` package will contain tools to work with asynchronous computations. + +The package contains sub-libraries with different utilities. + +### Zipping streams + +The "stream_zip.dart" sub-library contains functionality to combine several streams +of events into a single stream of tuples of events. + +### History. +This package is unrelated to the discontinued `async` package with version 0.1.17. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart new file mode 100644 index 00000000..e9d65441 --- /dev/null +++ b/pkgs/async/lib/async.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library dart.pkg.async; + +export "stream_zip.dart"; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml new file mode 100644 index 00000000..883118ab --- /dev/null +++ b/pkgs/async/pubspec.yaml @@ -0,0 +1,9 @@ +name: async +version: 0.9.0 +author: '"Dart Team "' +description: Utility functions and classes related to the 'dart:async' library. +homepage: http://www.dartlang.org +dev_dependencies: + unittest: ">=0.9.0 <0.10.0" +environment: + sdk: ">=1.0.0 <2.0.0" From 484c4432c7b5b41c647a034a3a2cd356b85d68ef Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 19 Dec 2013 12:12:44 +0000 Subject: [PATCH 002/260] Remove suprplus quoting from pubspec author fields. R=sgjesse@google.com Review URL: https://codereview.chromium.org//119093005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@31261 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 883118ab..36032260 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,6 +1,6 @@ name: async version: 0.9.0 -author: '"Dart Team "' +author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: http://www.dartlang.org dev_dependencies: From ffb9f58e9315fa5b43f62041d726ed4f3c1a0eaa Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 19 Dec 2013 12:36:15 +0000 Subject: [PATCH 003/260] Adding license files to packages. R=sgjesse@google.com Review URL: https://codereview.chromium.org//119203002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@31264 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/LICENSE | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 pkgs/async/LICENSE diff --git a/pkgs/async/LICENSE b/pkgs/async/LICENSE new file mode 100644 index 00000000..ee999303 --- /dev/null +++ b/pkgs/async/LICENSE @@ -0,0 +1,26 @@ +Copyright 2013, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 0f7eb8f41e7c908f26a683840fdaa23870acdbb4 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 19 Dec 2013 12:57:38 +0000 Subject: [PATCH 004/260] Add missing files from previous commit. Review URL: https://codereview.chromium.org//118783004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@31268 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/lib/stream_zip.dart | 119 +++++++++++ pkgs/async/test/stream_zip_test.dart | 231 ++++++++++++++++++++++ pkgs/async/test/stream_zip_zone_test.dart | 59 ++++++ 3 files changed, 409 insertions(+) create mode 100644 pkgs/async/lib/stream_zip.dart create mode 100644 pkgs/async/test/stream_zip_test.dart create mode 100644 pkgs/async/test/stream_zip_zone_test.dart diff --git a/pkgs/async/lib/stream_zip.dart b/pkgs/async/lib/stream_zip.dart new file mode 100644 index 00000000..055489db --- /dev/null +++ b/pkgs/async/lib/stream_zip.dart @@ -0,0 +1,119 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * Help for combining multiple streams into a single stream. + */ +library dart.pkg.async.stream_zip; + +import "dart:async"; + +/** + * A stream that combines the values of other streams. + */ +class StreamZip extends Stream { + final Iterable _streams; + StreamZip(Iterable streams) : _streams = streams; + + StreamSubscription listen(void onData(List data), { + Function onError, + void onDone(), + bool cancelOnError}) { + cancelOnError = identical(true, cancelOnError); + List subscriptions = []; + StreamController controller; + List current; + int dataCount = 0; + + /// Called for each data from a subscription in [subscriptions]. + void handleData(int index, data) { + current[index] = data; + dataCount++; + if (dataCount == subscriptions.length) { + List data = current; + current = new List(subscriptions.length); + dataCount = 0; + for (int i = 0; i < subscriptions.length; i++) { + if (i != index) subscriptions[i].resume(); + } + controller.add(data); + } else { + subscriptions[index].pause(); + } + } + + /// Called for each error from a subscription in [subscriptions]. + /// Except if [cancelOnError] is true, in which case the function below + /// is used instead. + void handleError(Object error, StackTrace stackTrace) { + controller.addError(error, stackTrace); + } + + /// Called when a subscription has an error and [cancelOnError] is true. + /// + /// Prematurely cancels all subscriptions since we know that we won't + /// be needing any more values. + void handleErrorCancel(Object error, StackTrace stackTrace) { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].cancel(); + } + controller.addError(error, stackTrace); + } + + void handleDone() { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].cancel(); + } + controller.close(); + } + + try { + for (Stream stream in _streams) { + int index = subscriptions.length; + subscriptions.add(stream.listen( + (data) { handleData(index, data); }, + onError: cancelOnError ? handleError : handleErrorCancel, + onDone: handleDone, + cancelOnError: cancelOnError)); + } + } catch (e) { + for (int i = subscriptions.length - 1; i >= 0; i--) { + subscriptions[i].cancel(); + } + rethrow; + } + + current = new List(subscriptions.length); + + controller = new StreamController( + onPause: () { + for (int i = 0; i < subscriptions.length; i++) { + // This may pause some subscriptions more than once. + // These will not be resumed by onResume below, but must wait for the + // next round. + subscriptions[i].pause(); + } + }, + onResume: () { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].resume(); + } + }, + onCancel: () { + for (int i = 0; i < subscriptions.length; i++) { + // Canceling more than once is safe. + subscriptions[i].cancel(); + } + } + ); + + if (subscriptions.isEmpty) { + controller.close(); + } + return controller.stream.listen(onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError); + } +} diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart new file mode 100644 index 00000000..54083b9d --- /dev/null +++ b/pkgs/async/test/stream_zip_test.dart @@ -0,0 +1,231 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:async"; +import "package:async/stream_zip.dart"; +import "package:unittest/unittest.dart"; + +/// Create an error with the same values as [base], except that it throwsA +/// when seeing the value [errorValue]. +Stream streamError(Stream base, int errorValue, error) { + return base.map((x) => (x == errorValue) ? throw error : x); +} + +/// Make a [Stream] from an [Iterable] by adding events to a stream controller +/// at periodic intervals. +Stream mks(Iterable iterable) { + Iterator iterator = iterable.iterator; + StreamController controller = new StreamController(); + // Some varying time between 3 and 10 ms. + int ms = ((++ctr) * 5) % 7 + 3; + new Timer.periodic(new Duration(milliseconds: ms), (Timer timer) { + if (iterator.moveNext()) { + controller.add(iterator.current); + } else { + controller.close(); + timer.cancel(); + } + }); + return controller.stream; +} + +/// Counter used to give varying delays for streams. +int ctr = 0; + +main() { + // Test that zipping [streams] gives the results iterated by [expectedData]. + testZip(Iterable streams, Iterable expectedData) { + List data = []; + Stream zip = new StreamZip(streams); + zip.listen(data.add, onDone: expectAsync0(() { + expect(data, equals(expectedData)); + })); + } + + test("Basic", () { + testZip([mks([1, 2, 3]), mks([4, 5, 6]), mks([7, 8, 9])], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + }); + + test("Uneven length 1", () { + testZip([mks([1, 2, 3, 99, 100]), mks([4, 5, 6]), mks([7, 8, 9])], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + }); + + test("Uneven length 2", () { + testZip([mks([1, 2, 3]), mks([4, 5, 6, 99, 100]), mks([7, 8, 9])], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + }); + + test("Uneven length 3", () { + testZip([mks([1, 2, 3]), mks([4, 5, 6]), mks([7, 8, 9, 99, 100])], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + }); + + test("Uneven length 4", () { + testZip([mks([1, 2, 3, 98]), mks([4, 5, 6]), mks([7, 8, 9, 99, 100])], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + }); + + test("Empty 1", () { + testZip([mks([]), mks([4, 5, 6]), mks([7, 8, 9])], []); + }); + + test("Empty 2", () { + testZip([mks([1, 2, 3]), mks([]), mks([7, 8, 9])], []); + }); + + test("Empty 3", () { + testZip([mks([1, 2, 3]), mks([4, 5, 6]), mks([])], []); + }); + + test("Empty source", () { + testZip([], []); + }); + + test("Single Source", () { + testZip([mks([1, 2, 3])], [[1], [2], [3]]); + }); + + test("Other-streams", () { + Stream st1 = mks([1, 2, 3, 4, 5, 6]).where((x) => x < 4); + Stream st2 = new Stream.periodic(const Duration(milliseconds: 5), + (x) => x + 4).take(3); + StreamController c = new StreamController.broadcast(); + Stream st3 = c.stream; + testZip([st1, st2, st3], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + c..add(7)..add(8)..add(9)..close(); + }); + + test("Error 1", () { + expect(new StreamZip([streamError(mks([1, 2, 3]), 2, "BAD-1"), + mks([4, 5, 6]), + mks([7, 8, 9])]).toList(), + throwsA(equals("BAD-1"))); + }); + + test("Error 2", () { + expect(new StreamZip([mks([1, 2, 3]), + streamError(mks([4, 5, 6]), 5, "BAD-2"), + mks([7, 8, 9])]).toList(), + throwsA(equals("BAD-2"))); + }); + + test("Error 3", () { + expect(new StreamZip([mks([1, 2, 3]), + mks([4, 5, 6]), + streamError(mks([7, 8, 9]), 8, "BAD-3")]).toList(), + throwsA(equals("BAD-3"))); + }); + + test("Error at end", () { + expect(new StreamZip([mks([1, 2, 3]), + streamError(mks([4, 5, 6]), 6, "BAD-4"), + mks([7, 8, 9])]).toList(), + throwsA(equals("BAD-4"))); + }); + + test("Error before first end", () { + // StreamControllers' streams with no "close" called will never be done, + // so the fourth event of the first stream is guaranteed to come first. + expect(new StreamZip( + [streamError(mks([1, 2, 3, 4]), 4, "BAD-5"), + (new StreamController()..add(4)..add(5)..add(6)).stream, + (new StreamController()..add(7)..add(8)..add(9)).stream] + ).toList(), + throwsA(equals("BAD-5"))); + }); + + test("Error after first end", () { + StreamController controller = new StreamController(); + controller..add(7)..add(8)..add(9); + // Transformer that puts error into controller when one of the first two + // streams have sent a done event. + StreamTransformer trans = new StreamTransformer.fromHandlers( + handleDone: (EventSink s) { + Timer.run(() { controller.addError("BAD-6"); }); + s.close(); + }); + testZip([mks([1, 2, 3]).transform(trans), + mks([4, 5, 6]).transform(trans), + controller.stream], + [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + }); + + test("Pause/Resume", () { + var done = expectAsync0((){}); // Call to complete test. + + int sc1p = 0; + StreamController c1 = new StreamController( + onPause: () { + sc1p++; + }, + onResume: () { + sc1p--; + }); + + int sc2p = 0; + StreamController c2 = new StreamController( + onPause: () { + sc2p++; + }, + onResume: () { + sc2p--; + }); + Stream zip = new StreamZip([c1.stream, c2.stream]); + + const ms25 = const Duration(milliseconds: 25); + + // StreamIterator uses pause and resume to control flow. + StreamIterator it = new StreamIterator(zip); + + it.moveNext().then((hasMore) { + expect(hasMore, isTrue); + expect(it.current, equals([1, 2])); + return it.moveNext(); + }).then((hasMore) { + expect(hasMore, isTrue); + expect(it.current, equals([3, 4])); + c2.add(6); + return it.moveNext(); + }).then((hasMore) { + expect(hasMore, isTrue); + expect(it.current, equals([5, 6])); + new Future.delayed(ms25).then((_) { c2.add(8); }); + return it.moveNext(); + }).then((hasMore) { + expect(hasMore, isTrue); + expect(it.current, equals([7, 8])); + c2.add(9); + return it.moveNext(); + }).then((hasMore) { + expect(hasMore, isFalse); + done(); + }); + + c1..add(1)..add(3)..add(5)..add(7)..close(); + c2..add(2)..add(4); + }); + + test("pause-resume2", () { + var s1 = new Stream.fromIterable([0, 2, 4, 6, 8]); + var s2 = new Stream.fromIterable([1, 3, 5, 7]); + var sz = new StreamZip([s1, s2]); + int ctr = 0; + var sub; + sub = sz.listen(expectAsync1((v) { + expect(v, equals([ctr * 2, ctr * 2 + 1])); + if (ctr == 1) { + sub.pause(new Future.delayed(const Duration(milliseconds: 25))); + } else if (ctr == 2) { + sub.pause(); + new Future.delayed(const Duration(milliseconds: 25)).then((_) { + sub.resume(); + }); + } + ctr++; + }, count: 4)); + }); +} diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart new file mode 100644 index 00000000..d39e3d72 --- /dev/null +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -0,0 +1,59 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:async"; +import "package:async/stream_zip.dart"; +import "package:unittest/unittest.dart"; + +// Test that stream listener callbacks all happen in the zone where the +// listen occurred. + +main() { + StreamController controller; + controller = new StreamController(); + testStream("singlesub-async", controller, controller.stream); + controller = new StreamController.broadcast(); + testStream("broadcast-async", controller, controller.stream); + controller = new StreamController(); + testStream("asbroadcast-async", controller, + controller.stream.asBroadcastStream()); + + controller = new StreamController(sync: true); + testStream("singlesub-sync", controller, controller.stream); + controller = new StreamController.broadcast(sync: true); + testStream("broadcast-sync", controller, controller.stream); + controller = new StreamController(sync: true); + testStream("asbroadcast-sync", controller, + controller.stream.asBroadcastStream()); +} + +void testStream(String name, StreamController controller, Stream stream) { + test(name, () { + Zone outer = Zone.current; + runZoned(() { + Zone newZone1 = Zone.current; + StreamSubscription sub; + sub = stream.listen(expectAsync1((v) { + expect(v, 42); + expect(Zone.current, newZone1); + outer.run(() { + sub.onData(expectAsync1((v) { + expect(v, 37); + expect(Zone.current, newZone1); + runZoned(() { + Zone newZone2 = Zone.current; + sub.onData(expectAsync1((v) { + expect(v, 87); + expect(Zone.current, newZone1); + })); + }); + controller.add(87); + })); + }); + controller.add(37); + })); + }); + controller.add(42); + }); +} From d881fb7c9184635876a408f5bfdc0d7aa065557a Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 19 Dec 2013 13:56:37 +0000 Subject: [PATCH 005/260] Fix incorrect reference to previous version of async library. R=sgjesse@google.com Review URL: https://codereview.chromium.org//119223002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@31274 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 6ca48752..dc34553b 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -8,4 +8,4 @@ The "stream_zip.dart" sub-library contains functionality to combine several stre of events into a single stream of tuples of events. ### History. -This package is unrelated to the discontinued `async` package with version 0.1.17. +This package is unrelated to the discontinued `async` package with version 0.1.7. From 53e7d442977ba091e650dc9d81b30bb5d9f21ec0 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 20 Dec 2013 07:58:49 +0000 Subject: [PATCH 006/260] Fix typo in failure-branch of stream iterator test. Also increment pkg/async version to fix broken first upload. Review URL: https://codereview.chromium.org//94013005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@31322 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 36032260..5c9a3286 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 0.9.0 +version: 0.9.1 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: http://www.dartlang.org From d6d5c321229c7aac522300c0fce65de7b3b20fe5 Mon Sep 17 00:00:00 2001 From: "kevmoo@google.com" Date: Fri, 28 Mar 2014 22:38:18 +0000 Subject: [PATCH 007/260] pkg/async: updates for unittest deprecations R=lrn@google.com Review URL: https://codereview.chromium.org//217113002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@34536 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/pubspec.yaml | 4 ++-- pkgs/async/test/stream_zip_test.dart | 6 +++--- pkgs/async/test/stream_zip_zone_test.dart | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 5c9a3286..f3442c28 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,9 +1,9 @@ name: async -version: 0.9.1 +version: 0.9.1+1 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: http://www.dartlang.org dev_dependencies: - unittest: ">=0.9.0 <0.10.0" + unittest: ">=0.10.0 <0.12.0" environment: sdk: ">=1.0.0 <2.0.0" diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 54083b9d..56a016a0 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -38,7 +38,7 @@ main() { testZip(Iterable streams, Iterable expectedData) { List data = []; Stream zip = new StreamZip(streams); - zip.listen(data.add, onDone: expectAsync0(() { + zip.listen(data.add, onDone: expectAsync(() { expect(data, equals(expectedData)); })); } @@ -155,7 +155,7 @@ main() { }); test("Pause/Resume", () { - var done = expectAsync0((){}); // Call to complete test. + var done = expectAsync((){}); // Call to complete test. int sc1p = 0; StreamController c1 = new StreamController( @@ -215,7 +215,7 @@ main() { var sz = new StreamZip([s1, s2]); int ctr = 0; var sub; - sub = sz.listen(expectAsync1((v) { + sub = sz.listen(expectAsync((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); if (ctr == 1) { sub.pause(new Future.delayed(const Duration(milliseconds: 25))); diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index d39e3d72..0b0c4bc5 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import "dart:async"; -import "package:async/stream_zip.dart"; import "package:unittest/unittest.dart"; // Test that stream listener callbacks all happen in the zone where the @@ -34,16 +33,16 @@ void testStream(String name, StreamController controller, Stream stream) { runZoned(() { Zone newZone1 = Zone.current; StreamSubscription sub; - sub = stream.listen(expectAsync1((v) { + sub = stream.listen(expectAsync((v) { expect(v, 42); expect(Zone.current, newZone1); outer.run(() { - sub.onData(expectAsync1((v) { + sub.onData(expectAsync((v) { expect(v, 37); expect(Zone.current, newZone1); runZoned(() { Zone newZone2 = Zone.current; - sub.onData(expectAsync1((v) { + sub.onData(expectAsync((v) { expect(v, 87); expect(Zone.current, newZone1); })); From 1ac41844fbda5756eb7fd76af0eac7d55f281d12 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 23 May 2014 08:41:10 +0000 Subject: [PATCH 008/260] Add capture/release for results in package:async R=floitsch@google.com Review URL: https://codereview.chromium.org//255123002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@36548 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/README.md | 12 +- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/result.dart | 278 +++++++++++++++++++++++++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/result_test.dart | 265 +++++++++++++++++++++++++++++ 5 files changed, 555 insertions(+), 3 deletions(-) create mode 100644 pkgs/async/lib/result.dart create mode 100644 pkgs/async/test/result_test.dart diff --git a/pkgs/async/README.md b/pkgs/async/README.md index dc34553b..73a40f44 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -4,8 +4,16 @@ The package contains sub-libraries with different utilities. ### Zipping streams -The "stream_zip.dart" sub-library contains functionality to combine several streams -of events into a single stream of tuples of events. +The "stream_zip.dart" sub-library contains functionality +to combine several streams of events into a single stream of tuples of events. + +### Results +The "result.dart" sub-library introduces a `Result` class that can hold either +a value or an error. +It allows capturing an asynchronous computation which can give either a value +or an error, into an asynchronous computation that always gives a `Result` +value, where errors can be treated as data. +It also allows releasing the `Result` back into an asynchronous computation. ### History. This package is unrelated to the discontinued `async` package with version 0.1.7. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index e9d65441..69c7a288 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -5,3 +5,4 @@ library dart.pkg.async; export "stream_zip.dart"; +export "result.dart"; diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart new file mode 100644 index 00000000..f76481c2 --- /dev/null +++ b/pkgs/async/lib/result.dart @@ -0,0 +1,278 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +/** + * Capture asynchronous results into synchronous values, and release them again. + * + * Capturing a result (either a returned value or a thrown error) + * means converting it into a [Result] - + * either a [ValueResult] or an [ErrorResult]. + * + * This value can release itself by writing itself either to a + * [EventSink] or a [Completer], or by becoming a [Future]. + */ +library dart.pkg.async.results; + +import "dart:async"; + +/** + * The result of a computation. + */ +abstract class Result { + /** + * Create a `Result` with the result of calling [computation]. + * + * This generates either a [ValueResult] with the value returned by + * calling `computation`, or an [ErrorResult] with an error thrown by + * the call. + */ + Result(T computation()) { + try { + return new ValueResult(computation()); + } catch (e, s) { + return new ErrorResult(e, s); + } + } + + /** + * Create a `Result` holding a value. + * + * Alias for [ValueResult.ValueResult]. + */ + factory Result.value(T value) = ValueResult; + + /** + * Create a `Result` holding an error. + * + * Alias for [ErrorResult.ErrorResult]. + */ + factory Result.error(Object error, [StackTrace stackTrace]) => + new ErrorResult(error, stackTrace); + + // Helper functions. + static _captureValue(value) => new ValueResult(value); + static _captureError(error, stack) => new ErrorResult(error, stack); + static _release(Result v) { + if (v.isValue) return v.asValue.value; // Avoid wrapping in future. + return v.asFuture; + } + + /** + * Capture the result of a future into a `Result` future. + * + * The resulting future will never have an error. + * Errors have been converted to an [ErrorResult] value. + */ + static Future capture(Future future) { + return future.then(_captureValue, onError: _captureError); + } + + /** + * Release the result of a captured future. + * + * Converts the [Result] value of the given [future] to a result or error + * completion of the returned future. + * + * If [future] completes with an error, the returned future completes with + * the same error. + */ + static Future release(Future future) { + return future.then(_release); + } + + /** + * Capture the results of a stream into a stream of [Result] values. + * + * The returned stream will not have any error events. + * Errors from the source stream have been converted to [ErrorResult]s. + * + * Shorthand for transforming the stream using [CaptureStreamTransformer]. + */ + static Stream captureStream(Stream source) { + return source.transform(const CaptureStreamTransformer()); + } + + /** + * Release a stream of [result] values into a stream of the results. + * + * `Result` values of the source stream become value or error events in + * the retuned stream as appropriate. + * Errors from the source stream become errors in the returned stream. + * + * Shorthand for transforming the stream using [ReleaseStreamTransformer]. + */ + static Stream releaseStream(Stream source) { + return source.transform(const ReleaseStreamTransformer()); + } + + /** + * Whether this result is a value result. + * + * Always the opposite of [isError]. + */ + bool get isValue; + + /** + * Whether this result is an error result. + * + * Always the opposite of [isValue]. + */ + bool get isError; + + /** + * If this is a value result, return itself. + * + * Otherwise return `null`. + */ + ValueResult get asValue; + + /** + * If this is an error result, return itself. + * + * Otherwise return `null`. + */ + ErrorResult get asError; + + /** + * Complete a completer with this result. + */ + void complete(Completer completer); + + /** + * Add this result to a [StreamSink]. + */ + void addTo(EventSink sink); + + /** + * Creates a future completed with this result as a value or an error. + */ + Future get asFuture; +} + +/** + * A result representing a returned value. + */ +class ValueResult implements Result { + /** The returned value that this result represents. */ + final T value; + /** Create a value result with the given [value]. */ + ValueResult(this.value); + bool get isValue => true; + bool get isError => false; + ValueResult get asValue => this; + ErrorResult get asError => null; + void complete(Completer completer) { + completer.complete(value); + } + void addTo(EventSink sink) { + sink.add(value); + } + Future get asFuture => new Future.value(value); +} + +/** + * A result representing a thrown error. + */ +class ErrorResult implements Result { + /** The thrown object that this result represents. */ + final error; + /** The stack trace, if any, associated with the throw. */ + final StackTrace stackTrace; + /** Create an error result with the given [error] and [stackTrace]. */ + ErrorResult(this.error, this.stackTrace); + bool get isValue => false; + bool get isError => true; + ValueResult get asValue => null; + ErrorResult get asError => this; + void complete(Completer completer) { + completer.completeError(error, stackTrace); + } + void addTo(EventSink sink) { + sink.addError(error, stackTrace); + } + Future get asFuture => new Future.error(error, stackTrace); +} + +/** + * A stream transformer that captures a stream of events into [Result]s. + * + * The result of the transformation is a stream of [Result] values and + * no error events. + */ +class CaptureStreamTransformer implements StreamTransformer> { + const CaptureStreamTransformer(); + + Stream> bind(Stream source) { + return new Stream>.eventTransformed(source, _createSink); + } + + static EventSink _createSink(EventSink sink) { + return new CaptureSink(sink); + } +} + +/** + * A stream transformer that releases a stream of result events. + * + * The result of the transformation is a stream of values and + * error events. + */ +class ReleaseStreamTransformer implements StreamTransformer, T> { + const ReleaseStreamTransformer(); + + Stream bind(Stream> source) { + return new Stream.eventTransformed(source, _createSink); + } + + static EventSink _createSink(EventSink sink) { + return new ReleaseSink(sink); + } +} + +/** + * An event sink wrapper that captures the incoming events. + * + * Wraps an [EventSink] that expects [Result] values. + * Accepts any value and error result, + * and passes them to the wrapped sink as [Result] values. + * + * The wrapped sink will never receive an error event. + */ +class CaptureSink implements EventSink { + final EventSink _sink; + + CaptureSink(EventSink> sink) : _sink = sink; + void add(T value) { _sink.add(new ValueResult(value)); } + void addError(Object error, StackTrace stackTrace) { + _sink.add(new ErrorResult(error, stackTrace)); + } + void close() { _sink.close(); } +} + +/** + * An event sink wrapper that releases the incoming result events. + * + * Wraps an output [EventSink] that expects any result. + * Accepts [Result] values, and puts the result value or error into the + * corresponding output sink add method. + */ +class ReleaseSink implements EventSink> { + final EventSink _sink; + ReleaseSink(EventSink sink) : _sink = sink; + void add(Result result) { + if (result.isValue) { + _sink.add(result.asValue.value); + } else { + ErrorResult error = result.asError; + _sink.addError(error.error, error.stackTrace); + } + } + void addError(Object error, StackTrace stackTrace) { + // Errors may be added by intermediate processing, even if it is never + // added by CaptureSink. + _sink.addError(error, stackTrace); + } + + void close() { _sink.close(); } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index f3442c28..9e5f3603 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 0.9.1+1 +version: 0.9.2 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: http://www.dartlang.org diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart new file mode 100644 index 00000000..53472ec9 --- /dev/null +++ b/pkgs/async/test/result_test.dart @@ -0,0 +1,265 @@ +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:async"; +import "dart:collection"; +import "package:async/result.dart"; +import "package:unittest/unittest.dart"; + +void main() { + StackTrace stack; + try { throw 0; } catch (e, s) { stack = s; } + + test("create result value", () { + Result result = new Result.value(42); + expect(result.isValue, isTrue); + expect(result.isError, isFalse); + ValueResult value = result.asValue; + expect(value.value, equals(42)); + }); + + test("create result value 2", () { + Result result = new ValueResult(42); + expect(result.isValue, isTrue); + expect(result.isError, isFalse); + ValueResult value = result.asValue; + expect(value.value, equals(42)); + }); + + test("create result error", () { + Result result = new Result.error("BAD", stack); + expect(result.isValue, isFalse); + expect(result.isError, isTrue); + ErrorResult error = result.asError; + expect(error.error, equals("BAD")); + expect(error.stackTrace, same(stack)); + }); + + test("create result error 2", () { + Result result = new ErrorResult("BAD", stack); + expect(result.isValue, isFalse); + expect(result.isError, isTrue); + ErrorResult error = result.asError; + expect(error.error, equals("BAD")); + expect(error.stackTrace, same(stack)); + }); + + test("create result error no stack", () { + Result result = new Result.error("BAD"); + expect(result.isValue, isFalse); + expect(result.isError, isTrue); + ErrorResult error = result.asError; + expect(error.error, equals("BAD")); + expect(error.stackTrace, isNull); + }); + + test("complete with value", () { + Result result = new ValueResult(42); + Completer c = new Completer(); + c.future.then(expectAsync((int v) { expect(v, equals(42)); }), + onError: (e, s) { fail("Unexpected error"); }); + result.complete(c); + }); + + test("complete with error", () { + Result result = new ErrorResult("BAD", stack); + Completer c = new Completer(); + c.future.then((bool v) { Expect.fail("Unexpected value $v"); }, + onError: expectAsync((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); + result.complete(c); + }); + + test("add sink value", () { + Result result = new ValueResult(42); + EventSink sink = new TestSink( + onData: expectAsync((v) { expect(v, equals(42)); }) + ); + result.addTo(sink); + }); + + test("add sink error", () { + Result result = new ErrorResult("BAD", stack); + EventSink sink = new TestSink( + onError: expectAsync((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + }) + ); + result.addTo(sink); + }); + + test("value as future", () { + Result result = new ValueResult(42); + result.asFuture.then(expectAsync((int v) { expect(v, equals(42)); }), + onError: (e, s) { fail("Unexpected error"); }); + }); + + test("error as future", () { + Result result = new ErrorResult("BAD", stack); + result.asFuture.then((bool v) { Expect.fail("Unexpected value $v"); }, + onError: expectAsync((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); + }); + + test("capture future value", () { + Future value = new Future.value(42); + Result.capture(value).then(expectAsync((Result result) { + expect(result.isValue, isTrue); + expect(result.isError, isFalse); + ValueResult value = result.asValue; + expect(value.value, equals(42)); + }), onError: (e, s) { + Expect.fail("Unexpected error: $e"); + }); + }); + + test("capture future error", () { + Future value = new Future.error("BAD", stack); + Result.capture(value).then(expectAsync((Result result) { + expect(result.isValue, isFalse); + expect(result.isError, isTrue); + ErrorResult error = result.asError; + expect(error.error, equals("BAD")); + expect(error.stackTrace, same(stack)); + }), onError: (e, s) { + Expect.fail("Unexpected error: $e"); + }); + }); + + test("release future value", () { + Future> future = + new Future>.value(new Result.value(42)); + Result.release(future).then(expectAsync((v) { + expect(v, equals(42)); + }), onError: (e, s) { + Expect.fail("Unexpected error: $e"); + }); + }); + + test("release future error", () { + // An error in the result is unwrapped and reified by release. + Future> future = + new Future>.value(new Result.error("BAD", stack)); + Result.release(future).then((v) { + Expect.fail("Unexpected value: $v"); + }, onError: expectAsync((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); + }); + + test("release future real error", () { + // An error in the error lane is passed through by release. + Future> future = new Future>.error("BAD", stack); + Result.release(future).then((v) { + Expect.fail("Unexpected value: $v"); + }, onError: expectAsync((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); + }); + + test("capture stream", () { + StreamController c = new StreamController(); + Stream stream = Result.captureStream(c.stream); + var expectedList = new Queue.from([new Result.value(42), + new Result.error("BAD", stack), + new Result.value(37)]); + void listener(Result actual) { + expect(expectedList.isEmpty, isFalse); + expectResult(actual, expectedList.removeFirst()); + } + stream.listen(expectAsync(listener, count: 3), + onError: (e, s) { fail("Unexpected error: $e"); }, + onDone: expectAsync((){}), + cancelOnError: true); + c.add(42); + c.addError("BAD", stack); + c.add(37); + c.close(); + }); + + test("release stream", () { + StreamController> c = new StreamController>(); + Stream stream = Result.releaseStream(c.stream); + List events = [new Result.value(42), + new Result.error("BAD", stack), + new Result.value(37)]; + // Expect the data events, and an extra error event. + var expectedList = new Queue.from(events)..add(new Result.error("BAD2")); + void dataListener(int v) { + expect(expectedList.isEmpty, isFalse); + Result expected = expectedList.removeFirst(); + expect(expected.isValue, isTrue); + expect(v, equals(expected.asValue.value)); + } + void errorListener(error, StackTrace stackTrace) { + expect(expectedList.isEmpty, isFalse); + Result expected = expectedList.removeFirst(); + expect(expected.isError, isTrue); + expect(error, equals(expected.asError.error)); + expect(stackTrace, same(expected.asError.stackTrace)); + } + stream.listen(expectAsync(dataListener, count: 2), + onError: expectAsync(errorListener, count: 2), + onDone: expectAsync((){})); + for (Result result in events) { + c.add(result); // Result value or error in data line. + } + c.addError("BAD2"); // Error in error line. + c.close(); + }); + + test("release stream cancel on error", () { + StreamController> c = new StreamController>(); + Stream stream = Result.releaseStream(c.stream); + stream.listen(expectAsync((v) { expect(v, equals(42)); }), + onError: expectAsync((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + }), + onDone: () { fail("Unexpected done event"); }, + cancelOnError: true); + c.add(new Result.value(42)); + c.add(new Result.error("BAD", stack)); + c.add(new Result.value(37)); + c.close(); + }); +} + +void expectResult(Result actual, Result expected) { + expect(actual.isValue, equals(expected.isValue)); + expect(actual.isError, equals(expected.isError)); + if (actual.isValue) { + expect(actual.asValue.value, equals(expected.asValue.value)); + } else { + expect(actual.asError.error, equals(expected.asError.error)); + expect(actual.asError.stackTrace, same(expected.asError.stackTrace)); + } +} + +class TestSink implements EventSink { + final Function onData; + final Function onError; + final Function onDone; + + TestSink({void this.onData(T data) : _nullData, + void this.onError(e, StackTrace s) : _nullError, + void this.onDone() : _nullDone }); + + void add(T value) { onData(value); } + void addError(error, [StackTrace stack]) { onError(error, stack); } + void close() { onDone(); } + + static void _nullData(value) { Expect.fail("Unexpected sink add: $value"); } + static void _nullError(e, StackTrace s) { + Expect.fail("Unexpected sink addError: $e"); + } + static void _nullDone() { Expect.fail("Unepxected sink close"); } +} From 4e191854ae3b95da6a54210a11b40b9f254ca1ad Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 23 May 2014 09:53:37 +0000 Subject: [PATCH 009/260] Add Result.flatten. Update version to 1.0.0 Fix typo in Result doc forgotten in previous CL. R=sgjesse@google.com Review URL: https://codereview.chromium.org//297033002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@36556 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/lib/result.dart | 17 +++++++++++++++-- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/result_test.dart | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart index f76481c2..903c8a53 100644 --- a/pkgs/async/lib/result.dart +++ b/pkgs/async/lib/result.dart @@ -27,7 +27,7 @@ abstract class Result { * calling `computation`, or an [ErrorResult] with an error thrown by * the call. */ - Result(T computation()) { + factory Result(T computation()) { try { return new ValueResult(computation()); } catch (e, s) { @@ -71,7 +71,7 @@ abstract class Result { /** * Release the result of a captured future. * - * Converts the [Result] value of the given [future] to a result or error + * Converts the [Result] value of the given [future] to a value or error * completion of the returned future. * * If [future] completes with an error, the returned future completes with @@ -106,6 +106,19 @@ abstract class Result { return source.transform(const ReleaseStreamTransformer()); } + /** + * Converts a result of a result to a single result. + * + * If the result is an error, or it is a `Result` value + * which is then an error, then a result with that error is returned. + * Otherwise both levels of results are value results, and a single + * result with the value is returned. + */ + static Result flatten(Result result) { + if (result.isError) return result; + return result.asValue.value; + } + /** * Whether this result is a value result. * diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 9e5f3603..65539f92 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 0.9.2 +version: 1.0.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: http://www.dartlang.org diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 53472ec9..d101b489 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -231,6 +231,26 @@ void main() { c.add(new Result.value(37)); c.close(); }); + + + test("flatten error 1", () { + Result error = new Result.error("BAD", stack); + Result flattened = Result.flatten(error); + expectResult(flattened, error); + }); + + test("flatten error 2", () { + Result error = new Result.error("BAD", stack); + Result> result = new Result>.value(error); + Result flattened = Result.flatten(result); + expectResult(flattened, error); + }); + + test("flatten value", () { + Result> result = + new Result>.value(new Result.value(42)); + expectResult(Result.flatten(result), new Result.value(42)); + }); } void expectResult(Result actual, Result expected) { From d81fd250f026e7c828007664e035026395b91412 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 23 May 2014 10:09:36 +0000 Subject: [PATCH 010/260] Fix analyzer warnings! R=sgjesse@google.com Review URL: https://codereview.chromium.org//292403002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@36558 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/lib/result.dart | 10 +++++----- pkgs/async/test/result_test.dart | 25 +++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart index 903c8a53..db04ae71 100644 --- a/pkgs/async/lib/result.dart +++ b/pkgs/async/lib/result.dart @@ -198,10 +198,10 @@ class ErrorResult implements Result { bool get isError => true; ValueResult get asValue => null; ErrorResult get asError => this; - void complete(Completer completer) { + void complete(Completer completer) { completer.completeError(error, stackTrace); } - void addTo(EventSink sink) { + void addTo(EventSink sink) { sink.addError(error, stackTrace); } Future get asFuture => new Future.error(error, stackTrace); @@ -238,7 +238,7 @@ class ReleaseStreamTransformer implements StreamTransformer, T> { return new Stream.eventTransformed(source, _createSink); } - static EventSink _createSink(EventSink sink) { + static EventSink _createSink(EventSink sink) { return new ReleaseSink(sink); } } @@ -257,7 +257,7 @@ class CaptureSink implements EventSink { CaptureSink(EventSink> sink) : _sink = sink; void add(T value) { _sink.add(new ValueResult(value)); } - void addError(Object error, StackTrace stackTrace) { + void addError(Object error, [StackTrace stackTrace]) { _sink.add(new ErrorResult(error, stackTrace)); } void close() { _sink.close(); } @@ -281,7 +281,7 @@ class ReleaseSink implements EventSink> { _sink.addError(error.error, error.stackTrace); } } - void addError(Object error, StackTrace stackTrace) { + void addError(Object error, [StackTrace stackTrace]) { // Errors may be added by intermediate processing, even if it is never // added by CaptureSink. _sink.addError(error, stackTrace); diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index d101b489..da6746f4 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -37,7 +37,7 @@ void main() { }); test("create result error 2", () { - Result result = new ErrorResult("BAD", stack); + Result result = new ErrorResult("BAD", stack); expect(result.isValue, isFalse); expect(result.isError, isTrue); ErrorResult error = result.asError; @@ -65,7 +65,7 @@ void main() { test("complete with error", () { Result result = new ErrorResult("BAD", stack); Completer c = new Completer(); - c.future.then((bool v) { Expect.fail("Unexpected value $v"); }, + c.future.then((bool v) { fail("Unexpected value $v"); }, onError: expectAsync((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); @@ -100,7 +100,7 @@ void main() { test("error as future", () { Result result = new ErrorResult("BAD", stack); - result.asFuture.then((bool v) { Expect.fail("Unexpected value $v"); }, + result.asFuture.then((bool v) { fail("Unexpected value $v"); }, onError: expectAsync((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); @@ -115,7 +115,7 @@ void main() { ValueResult value = result.asValue; expect(value.value, equals(42)); }), onError: (e, s) { - Expect.fail("Unexpected error: $e"); + fail("Unexpected error: $e"); }); }); @@ -128,7 +128,7 @@ void main() { expect(error.error, equals("BAD")); expect(error.stackTrace, same(stack)); }), onError: (e, s) { - Expect.fail("Unexpected error: $e"); + fail("Unexpected error: $e"); }); }); @@ -138,7 +138,7 @@ void main() { Result.release(future).then(expectAsync((v) { expect(v, equals(42)); }), onError: (e, s) { - Expect.fail("Unexpected error: $e"); + fail("Unexpected error: $e"); }); }); @@ -147,7 +147,7 @@ void main() { Future> future = new Future>.value(new Result.error("BAD", stack)); Result.release(future).then((v) { - Expect.fail("Unexpected value: $v"); + fail("Unexpected value: $v"); }, onError: expectAsync((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); @@ -158,7 +158,7 @@ void main() { // An error in the error lane is passed through by release. Future> future = new Future>.error("BAD", stack); Result.release(future).then((v) { - Expect.fail("Unexpected value: $v"); + fail("Unexpected value: $v"); }, onError: expectAsync((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); @@ -235,7 +235,8 @@ void main() { test("flatten error 1", () { Result error = new Result.error("BAD", stack); - Result flattened = Result.flatten(error); + Result flattened = + Result.flatten(new Result>.error(error)); expectResult(flattened, error); }); @@ -277,9 +278,9 @@ class TestSink implements EventSink { void addError(error, [StackTrace stack]) { onError(error, stack); } void close() { onDone(); } - static void _nullData(value) { Expect.fail("Unexpected sink add: $value"); } + static void _nullData(value) { fail("Unexpected sink add: $value"); } static void _nullError(e, StackTrace s) { - Expect.fail("Unexpected sink addError: $e"); + fail("Unexpected sink addError: $e"); } - static void _nullDone() { Expect.fail("Unepxected sink close"); } + static void _nullDone() { fail("Unepxected sink close"); } } From befb38e30bb276cb834fe3ff96f425e808e05ed5 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 23 May 2014 10:14:29 +0000 Subject: [PATCH 011/260] Fix typo in fix. Review URL: https://codereview.chromium.org//294973010 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@36560 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/test/result_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index da6746f4..0f2ae0a2 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -236,7 +236,7 @@ void main() { test("flatten error 1", () { Result error = new Result.error("BAD", stack); Result flattened = - Result.flatten(new Result>.error(error)); + Result.flatten(new Result>.error("BAD", stack)); expectResult(flattened, error); }); From 61e394d624f39276546fdc556d3c8fc3a6888e83 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Mon, 23 Feb 2015 10:36:58 +0000 Subject: [PATCH 012/260] Make synchronous broadcast StreamController throw if adding event while adding event. It already throws if there is more than one listener, but this change also makes it throw if there is only one listener, for consistency. Also add SynchronousStreamController abstract class. It only contains documentation for synchronous stream controllers. BUG= http://dartbug.com/22240 R=floitsch@google.com Review URL: https://codereview.chromium.org//882713009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/async@43946 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_zip_zone_test.dart | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 65539f92..0f17e33d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.0.0 +version: 1.1.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: http://www.dartlang.org diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index 0b0c4bc5..70851b4f 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -47,10 +47,18 @@ void testStream(String name, StreamController controller, Stream stream) { expect(Zone.current, newZone1); })); }); - controller.add(87); + if (controller is SynchronousStreamController) { + scheduleMicrotask(() => controller.add(87)); + } else { + controller.add(87); + } })); }); - controller.add(37); + if (controller is SynchronousStreamController) { + scheduleMicrotask(() => controller.add(37)); + } else { + controller.add(37); + } })); }); controller.add(42); From fbb434274a6e01b4fdc05587be7cf8c6399b1f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Gjesse?= Date: Tue, 24 Feb 2015 11:27:40 +0100 Subject: [PATCH 013/260] Update project to match OSS template R=ricow@google.com BUG= Review URL: https://codereview.chromium.org//954703002 --- pkgs/async/.gitignore | 8 ++++++++ pkgs/async/.status | 4 ++++ pkgs/async/AUTHORS | 6 ++++++ pkgs/async/CHANGELOG.md | 5 +++++ pkgs/async/CONTRIBUTING.md | 33 +++++++++++++++++++++++++++++++++ pkgs/async/LICENSE | 2 +- pkgs/async/README.md | 8 ++++++++ pkgs/async/codereview.settings | 3 +++ pkgs/async/pubspec.yaml | 2 +- 9 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 pkgs/async/.gitignore create mode 100644 pkgs/async/.status create mode 100644 pkgs/async/AUTHORS create mode 100644 pkgs/async/CHANGELOG.md create mode 100644 pkgs/async/CONTRIBUTING.md create mode 100644 pkgs/async/codereview.settings diff --git a/pkgs/async/.gitignore b/pkgs/async/.gitignore new file mode 100644 index 00000000..89f7747c --- /dev/null +++ b/pkgs/async/.gitignore @@ -0,0 +1,8 @@ +.buildlog +.DS_Store +.idea +.pub/ +.settings/ +build/ +packages +pubspec.lock diff --git a/pkgs/async/.status b/pkgs/async/.status new file mode 100644 index 00000000..364ca4b4 --- /dev/null +++ b/pkgs/async/.status @@ -0,0 +1,4 @@ +# Copyright (c) 2015, 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. + diff --git a/pkgs/async/AUTHORS b/pkgs/async/AUTHORS new file mode 100644 index 00000000..e8063a8c --- /dev/null +++ b/pkgs/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/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md new file mode 100644 index 00000000..d1b66d0d --- /dev/null +++ b/pkgs/async/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 1.1.0 + +- ChangeLog starts here. diff --git a/pkgs/async/CONTRIBUTING.md b/pkgs/async/CONTRIBUTING.md new file mode 100644 index 00000000..6f5e0ea6 --- /dev/null +++ b/pkgs/async/CONTRIBUTING.md @@ -0,0 +1,33 @@ +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](https://cla.developers.google.com/about/google-individual) +(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. + +### Code reviews +All submissions, including submissions by project members, require review. + +### File headers +All files in the project must start with the following header. + + // Copyright (c) 2015, 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](https://developers.google.com/open-source/cla/corporate). diff --git a/pkgs/async/LICENSE b/pkgs/async/LICENSE index ee999303..de31e1a0 100644 --- a/pkgs/async/LICENSE +++ b/pkgs/async/LICENSE @@ -1,4 +1,4 @@ -Copyright 2013, the Dart project authors. All rights reserved. +Copyright 2015, the Dart project authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 73a40f44..49ea5202 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -1,3 +1,5 @@ +# Async utilities package + The `async` package will contain tools to work with asynchronous computations. The package contains sub-libraries with different utilities. @@ -17,3 +19,9 @@ It also allows releasing the `Result` back into an asynchronous computation. ### History. This package is unrelated to the discontinued `async` package with version 0.1.7. + +## Features and bugs + +Please file feature requests and bugs at the [issue tracker][tracker]. + +[tracker]: https://github.com/dart-lang/async/issues diff --git a/pkgs/async/codereview.settings b/pkgs/async/codereview.settings new file mode 100644 index 00000000..af9ef31b --- /dev/null +++ b/pkgs/async/codereview.settings @@ -0,0 +1,3 @@ +CODE_REVIEW_SERVER: http://codereview.chromium.org/ +VIEW_VC: https://github.com/dart-lang/async/commit/ +CC_LIST: reviews@dartlang.org diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0f17e33d..4dc136d7 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -2,7 +2,7 @@ name: async version: 1.1.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. -homepage: http://www.dartlang.org +homepage: https://www.github.com/dart-lang/async dev_dependencies: unittest: ">=0.10.0 <0.12.0" environment: From 3dd4aed600c8282cc67b497b4df4f3e27f62def3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Gjesse?= Date: Tue, 24 Feb 2015 15:06:01 +0100 Subject: [PATCH 014/260] Update SDK constraint to at lease 1.9.0 The type SynchronousStreamController which was added in 1.9.0 is used in one of the tests. R=lrn@google.com, ricow@google.com BUG= Review URL: https://codereview.chromium.org//935453004 --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d1b66d0d..f78f6c55 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.1.1 + +- Updated SDK version constraint to at least 1.9.0. + ## 1.1.0 - ChangeLog starts here. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 4dc136d7..9ac50300 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,9 +1,9 @@ name: async -version: 1.1.0 +version: 1.1.1 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async dev_dependencies: unittest: ">=0.10.0 <0.12.0" environment: - sdk: ">=1.0.0 <2.0.0" + sdk: ">=1.9.0 <2.0.0" From c46a6189b301c741fb523e3ddd37a7719efb3983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Gjesse?= Date: Tue, 24 Feb 2015 15:27:04 +0100 Subject: [PATCH 015/260] Update .status file for async package R=ricow@google.com BUG= Review URL: https://codereview.chromium.org//948243004 --- pkgs/async/.status | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkgs/async/.status b/pkgs/async/.status index 364ca4b4..ebc5f813 100644 --- a/pkgs/async/.status +++ b/pkgs/async/.status @@ -2,3 +2,11 @@ # 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. +# Skip non-test files ending with "_test". +packages/*: Skip +*/packages/*: Skip +*/*/packages/*: Skip +*/*/*/packages/*: Skip +*/*/*/*packages/*: Skip +*/*/*/*/*packages/*: Skip + From 94a76a88c6a1198ed4b0210cb37d4f047a377dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Gjesse?= Date: Tue, 24 Feb 2015 17:08:35 +0100 Subject: [PATCH 016/260] Allow SDK version 1.9.0-dev.8.4 TBR=ricow@google.com, lrn@google.com BUG= Review URL: https://codereview.chromium.org//949723003 --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 9ac50300..21db4f7d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -6,4 +6,4 @@ homepage: https://www.github.com/dart-lang/async dev_dependencies: unittest: ">=0.10.0 <0.12.0" environment: - sdk: ">=1.9.0 <2.0.0" + sdk: ">=1.9.0-dev.8.4 <2.0.0" From 5ab01ae5190994d43ce7d3a514201246785e921f Mon Sep 17 00:00:00 2001 From: Seth Ladd Date: Sun, 29 Mar 2015 07:26:18 -0700 Subject: [PATCH 017/260] Update README.md --- pkgs/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 49ea5202..55bea7f3 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -1,6 +1,6 @@ # Async utilities package -The `async` package will contain tools to work with asynchronous computations. +Contains tools to work with asynchronous computations. The package contains sub-libraries with different utilities. From 55d73a64f9e9ffc465d8252bd69167dbbe94f2d8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 11 Jun 2015 13:27:56 -0700 Subject: [PATCH 018/260] Upgrade to the new test runner. This also fixes an issue in result_test where the a null stack was being compared against. R=lrn@google.com Review URL: https://codereview.chromium.org//1180513004. --- pkgs/async/pubspec.yaml | 5 +++-- pkgs/async/test/result_test.dart | 15 ++++++++++----- pkgs/async/test/stream_zip_test.dart | 2 +- pkgs/async/test/stream_zip_zone_test.dart | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 21db4f7d..a05d2e5c 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,9 +1,10 @@ name: async -version: 1.1.1 +version: 1.1.2-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async dev_dependencies: - unittest: ">=0.10.0 <0.12.0" + test: "^0.12.0" + stack_trace: "^1.0.0" environment: sdk: ">=1.9.0-dev.8.4 <2.0.0" diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 0f2ae0a2..656d5638 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -4,12 +4,13 @@ import "dart:async"; import "dart:collection"; + import "package:async/result.dart"; -import "package:unittest/unittest.dart"; +import "package:stack_trace/stack_trace.dart"; +import "package:test/test.dart"; void main() { - StackTrace stack; - try { throw 0; } catch (e, s) { stack = s; } + var stack = new Trace.current(); test("create result value", () { Result result = new Result.value(42); @@ -192,13 +193,16 @@ void main() { new Result.error("BAD", stack), new Result.value(37)]; // Expect the data events, and an extra error event. - var expectedList = new Queue.from(events)..add(new Result.error("BAD2")); + var expectedList = new Queue.from(events) + ..add(new Result.error("BAD2", stack)); + void dataListener(int v) { expect(expectedList.isEmpty, isFalse); Result expected = expectedList.removeFirst(); expect(expected.isValue, isTrue); expect(v, equals(expected.asValue.value)); } + void errorListener(error, StackTrace stackTrace) { expect(expectedList.isEmpty, isFalse); Result expected = expectedList.removeFirst(); @@ -206,13 +210,14 @@ void main() { expect(error, equals(expected.asError.error)); expect(stackTrace, same(expected.asError.stackTrace)); } + stream.listen(expectAsync(dataListener, count: 2), onError: expectAsync(errorListener, count: 2), onDone: expectAsync((){})); for (Result result in events) { c.add(result); // Result value or error in data line. } - c.addError("BAD2"); // Error in error line. + c.addError("BAD2", stack); // Error in error line. c.close(); }); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 56a016a0..9606b924 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -4,7 +4,7 @@ import "dart:async"; import "package:async/stream_zip.dart"; -import "package:unittest/unittest.dart"; +import "package:test/test.dart"; /// Create an error with the same values as [base], except that it throwsA /// when seeing the value [errorValue]. diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index 70851b4f..cdf2e164 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import "dart:async"; -import "package:unittest/unittest.dart"; +import "package:test/test.dart"; // Test that stream listener callbacks all happen in the zone where the // listen occurred. From 0fafe076151773e27e3f7d498a572bdc62ff1a81 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 12 Jun 2015 17:06:40 -0700 Subject: [PATCH 019/260] Add a FutureGroup class. This class has been copied around in various forms for ages; see for example dart-lang/sdkdart-lang/async#6626. This is more or less a full rewrite with tests added. R=lrn@google.com Review URL: https://codereview.chromium.org//1179053003. --- pkgs/async/CHANGELOG.md | 5 +- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/future_group.dart | 77 +++++++++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/future_group_test.dart | 124 +++++++++++++++++++++++++ 5 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 pkgs/async/lib/src/future_group.dart create mode 100644 pkgs/async/test/future_group_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index f78f6c55..125519b5 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,7 @@ -# Changelog +## 1.2.0 + +- Added a `FutureGroup` class for waiting for a group of futures, potentially of + unknown size, to complete. ## 1.1.1 diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 69c7a288..7dbda70a 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -4,5 +4,6 @@ library dart.pkg.async; +export "src/future_group.dart"; export "stream_zip.dart"; export "result.dart"; diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart new file mode 100644 index 00000000..81ba4fae --- /dev/null +++ b/pkgs/async/lib/src/future_group.dart @@ -0,0 +1,77 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.future_group; + +import 'dart:async'; + +/// A collection of futures waits until all added [Future]s complete. +/// +/// Futures are added to the group with [add]. Once you're finished adding +/// futures, signal that by calling [close]. Then, once all added futures have +/// completed, [future] will complete with a list of values from the futures in +/// the group, in the order they were added. +/// +/// If any added future completes with an error, [future] will emit that error +/// and the group will be closed, regardless of the state of other futures in +/// the group. +/// +/// This is similar to [Future.wait] with `eagerError` set to `true`, except +/// that a [FutureGroup] can have futures added gradually over time rather than +/// needing them all at once. +class FutureGroup implements Sink> { + /// The number of futures that have yet to complete. + var _pending = 0; + + /// Whether [close] has been called. + var _closed = false; + + /// The future that fires once [close] has been called and all futures in the + /// group have completed. + /// + /// This will also complete with an error if any of the futures in the group + /// fails, regardless of whether [close] was called. + Future> get future => _completer.future; + final _completer = new Completer>(); + + /// The values emitted by the futures that have been added to the group, in + /// the order they were added. + /// + /// The slots for futures that haven't completed yet are `null`. + final _values = new List(); + + /// Wait for [task] to complete. + void add(Future task) { + if (_closed) throw new StateError("The FutureGroup is closed."); + + // Ensure that future values are put into [values] in the same order they're + // added to the group by pre-allocating a slot for them and recording its + // index. + var index = _values.length; + _values.add(null); + + _pending++; + task.then((value) { + if (_completer.isCompleted) return; + + _pending--; + _values[index] = value; + + if (_pending == 0 && _closed) _completer.complete(_values); + }).catchError((error, stackTrace) { + if (_completer.isCompleted) return; + _completer.completeError(error, stackTrace); + }); + } + + /// Signals to the group that the caller is done adding futures, and so + /// [future] should fire once all added futures have completed. + void close() { + _closed = true; + if (_pending != 0) return; + if (_completer.isCompleted) return; + _completer.complete(_values); + } +} + diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index a05d2e5c..102f2d44 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.1.2-dev +version: 1.2.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart new file mode 100644 index 00000000..f5350805 --- /dev/null +++ b/pkgs/async/test/future_group_test.dart @@ -0,0 +1,124 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/src/future_group.dart'; +import 'package:test/test.dart'; + +void main() { + var futureGroup; + setUp(() { + futureGroup = new FutureGroup(); + }); + + group("with no futures", () { + test("never completes if nothing happens", () async { + var completed = false; + futureGroup.future.then((_) => completed = true); + + await new Future.delayed(Duration.ZERO); + expect(completed, isFalse); + }); + + test("completes once it's closed", () { + expect(futureGroup.future, completion(isEmpty)); + futureGroup.close(); + }); + }); + + group("with a future that already completed", () { + test("never completes if nothing happens", () async { + futureGroup.add(new Future.value()); + await new Future.delayed(Duration.ZERO); + + var completed = false; + futureGroup.future.then((_) => completed = true); + + await new Future.delayed(Duration.ZERO); + expect(completed, isFalse); + }); + + test("completes once it's closed", () async { + futureGroup.add(new Future.value()); + await new Future.delayed(Duration.ZERO); + + expect(futureGroup.future, completes); + futureGroup.close(); + }); + + test("completes to that future's value", () { + futureGroup.add(new Future.value(1)); + futureGroup.close(); + expect(futureGroup.future, completion(equals([1]))); + }); + + test("completes to that future's error, even if it's not closed", () { + futureGroup.add(new Future.error("error")); + expect(futureGroup.future, throwsA("error")); + }); + }); + + test("completes once all contained futures complete", () async { + var futureGroup = new FutureGroup(); + var completer1 = new Completer(); + var completer2 = new Completer(); + var completer3 = new Completer(); + + futureGroup.add(completer1.future); + futureGroup.add(completer2.future); + futureGroup.add(completer3.future); + futureGroup.close(); + + var completed = false; + futureGroup.future.then((_) => completed = true); + + completer1.complete(); + await new Future.delayed(Duration.ZERO); + expect(completed, isFalse); + + completer2.complete(); + await new Future.delayed(Duration.ZERO); + expect(completed, isFalse); + + completer3.complete(); + await new Future.delayed(Duration.ZERO); + expect(completed, isTrue); + }); + + test("completes to the values of the futures in order of addition", () { + var futureGroup = new FutureGroup(); + var completer1 = new Completer(); + var completer2 = new Completer(); + var completer3 = new Completer(); + + futureGroup.add(completer1.future); + futureGroup.add(completer2.future); + futureGroup.add(completer3.future); + futureGroup.close(); + + // Complete the completers in reverse order to prove that that doesn't + // affect the result order. + completer3.complete(3); + completer2.complete(2); + completer1.complete(1); + expect(futureGroup.future, completion(equals([1, 2, 3]))); + }); + + test("completes to the first error to be emitted, even if it's not closed", + () { + var futureGroup = new FutureGroup(); + var completer1 = new Completer(); + var completer2 = new Completer(); + var completer3 = new Completer(); + + futureGroup.add(completer1.future); + futureGroup.add(completer2.future); + futureGroup.add(completer3.future); + + completer2.completeError("error 2"); + completer1.completeError("error 1"); + expect(futureGroup.future, throwsA("error 2")); + }); +} From 62e6fcb8488925b462c0ef87758b03c83aac4380 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 19 Jun 2015 13:01:24 -0700 Subject: [PATCH 020/260] Add a StreamGroup class for merging streams. R=lrn@google.com Review URL: https://codereview.chromium.org//1178793006. --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/stream_group.dart | 259 +++++++++ pkgs/async/test/stream_group_test.dart | 724 +++++++++++++++++++++++++ 4 files changed, 987 insertions(+) create mode 100644 pkgs/async/lib/src/stream_group.dart create mode 100644 pkgs/async/test/stream_group_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 125519b5..1333fe37 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,9 @@ - Added a `FutureGroup` class for waiting for a group of futures, potentially of unknown size, to complete. +- Added a `StreamGroup` class for merging the events of a group of streams, + potentially of unknown size. + ## 1.1.1 - Updated SDK version constraint to at least 1.9.0. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 7dbda70a..345b8dcf 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -5,5 +5,6 @@ library dart.pkg.async; export "src/future_group.dart"; +export "src/stream_group.dart"; export "stream_zip.dart"; export "result.dart"; diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart new file mode 100644 index 00000000..01477878 --- /dev/null +++ b/pkgs/async/lib/src/stream_group.dart @@ -0,0 +1,259 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_group; + +import 'dart:async'; + +/// A collection of streams whose events are unified and sent through a central +/// stream. +/// +/// Both errors and data events are forwarded through [stream]. The streams in +/// the group won't be listened to until [stream] has a listener. **Note that +/// this means that events emitted by broadcast streams will be dropped until +/// [stream] has a listener.** +/// +/// If the `StreamGroup` is construced using [new StreamGroup], [stream] will be +/// single-subscription. In this case, if [stream] is paused or canceled, all +/// streams in the group will likewise be paused or canceled, respectively. +/// +/// If the `StreamGroup` is construced using [new StreamGroup.broadcast], +/// [stream] will be a broadcast stream. In this case, the streams in the group +/// will never be paused and single-subscription streams in the group will never +/// be canceled. **Note that single-subscription streams in a broadcast group +/// may drop events if a listener is added and later removed.** Broadcast +/// streams in the group will be canceled once [stream] has no listeners, and +/// will be listened to again once [stream] has listeners. +/// +/// [stream] won't close until [close] is called on the group *and* every stream +/// in the group closes. +class StreamGroup implements Sink> { + /// The stream through which all events from streams in the group are emitted. + Stream get stream => _controller.stream; + StreamController _controller; + + /// Whether the group is closed, meaning that no more streams may be added. + var _closed = false; + + /// The current state of the group. + /// + /// See [_StreamGroupState] for detailed descriptions of each state. + var _state = _StreamGroupState.dormant; + + /// Streams that have been added to the group, and their subscriptions if they + /// have been subscribed to. + /// + /// The subscriptions will be null until the group has a listener registered. + /// If it's a broadcast group and it goes dormant again, broadcast stream + /// subscriptions will be canceled and set to null again. Single-subscriber + /// stream subscriptions will be left intact, since they can't be + /// re-subscribed. + final _subscriptions = new Map, StreamSubscription>(); + + /// Merges the events from [streams] into a single (single-subscriber) stream. + /// + /// This is equivalent to adding [streams] to a group, closing that group, and + /// returning its stream. + static Stream merge(Iterable streams) { + var group = new StreamGroup(); + streams.forEach(group.add); + group.close(); + return group.stream; + } + + /// Creates a new stream group where [stream] is single-subscriber. + StreamGroup() { + _controller = new StreamController( + onListen: _onListen, + onPause: _onPause, + onResume: _onResume, + onCancel: _onCancel, + sync: true); + } + + /// Creates a new stream group where [stream] is a broadcast stream. + StreamGroup.broadcast() { + _controller = new StreamController.broadcast( + onListen: _onListen, + onCancel: _onCancelBroadcast, + sync: true); + } + + /// Adds [stream] as a member of this group. + /// + /// Any events from [stream] will be emitted through [this.stream]. If this + /// group has a listener, [stream] will be listened to immediately; otherwise + /// it will only be listened to once this group gets a listener. + /// + /// If this is a single-subscription group and its subscription has been + /// canceled, [stream] will be canceled as soon as its added. If this returns + /// a [Future], it will be returned from [add]. Otherwise, [add] returns + /// `null`. + /// + /// Throws a [StateError] if this group is closed. + Future add(Stream stream) { + if (_closed) { + throw new StateError("Can't add a Stream to a closed StreamGroup."); + } + + if (_state == _StreamGroupState.dormant) { + _subscriptions.putIfAbsent(stream, () => null); + } else if (_state == _StreamGroupState.canceled) { + // Listen to the stream and cancel it immediately so that no one else can + // listen, for consistency. If the stream has an onCancel listener this + // will also fire that, which may help it clean up resources. + return stream.listen(null).cancel(); + } else { + _subscriptions.putIfAbsent(stream, () => _listenToStream(stream)); + } + + return null; + } + + /// Removes [stream] as a member of this group. + /// + /// No further events from [stream] will be emitted through this group. If + /// [stream] has been listened to, its subscription will be canceled. + /// + /// If [stream] has been listened to, this *synchronously* cancels its + /// subscription. This means that any events from [stream] that haven't yet + /// been emitted through this group will not be. + /// + /// If [stream]'s subscription is canceled, this returns + /// [StreamSubscription.cancel]'s return value. Otherwise, it returns `null`. + Future remove(Stream stream) { + var subscription = _subscriptions.remove(stream); + var future = subscription == null ? null : subscription.cancel(); + if (_closed && _subscriptions.isEmpty) _controller.close(); + return future; + } + + /// A callback called when [stream] is listened to. + /// + /// This is called for both single-subscription and broadcast groups. + void _onListen() { + _state = _StreamGroupState.listening; + _subscriptions.forEach((stream, subscription) { + // If this is a broadcast group and this isn't the first time it's been + // listened to, there may still be some subscriptions to + // single-subscription streams. + if (subscription != null) return; + _subscriptions[stream] = _listenToStream(stream); + }); + } + + /// A callback called when [stream] is paused. + void _onPause() { + _state = _StreamGroupState.paused; + for (var subscription in _subscriptions.values) { + subscription.pause(); + } + } + + /// A callback called when [stream] is resumed. + void _onResume() { + _state = _StreamGroupState.listening; + for (var subscription in _subscriptions.values) { + subscription.resume(); + } + } + + /// A callback called when [stream] is canceled. + /// + /// This is only called for single-subscription groups. + Future _onCancel() { + _state = _StreamGroupState.canceled; + + var futures = _subscriptions.values + .map((subscription) => subscription.cancel()) + .where((future) => future != null) + .toList(); + + _subscriptions.clear(); + return futures.isEmpty ? null : Future.wait(futures); + } + + /// A callback called when [stream]'s last listener is canceled. + /// + /// This is only called for broadcast groups. + void _onCancelBroadcast() { + _state = _StreamGroupState.dormant; + + _subscriptions.forEach((stream, subscription) { + // Cancel the broadcast streams, since we can re-listen to those later, + // but allow the single-subscription streams to keep firing. Their events + // will still be added to [_controller], but then they'll be dropped since + // it has no listeners. + if (!stream.isBroadcast) return; + subscription.cancel(); + _subscriptions[stream] = null; + }); + } + + /// Starts actively forwarding events from [stream] to [_controller]. + /// + /// This will pause the resulting subscription if [this] is paused. + StreamSubscription _listenToStream(Stream stream) { + var subscription = stream.listen( + _controller.add, + onError: _controller.addError, + onDone: () => remove(stream)); + if (_state == _StreamGroupState.paused) subscription.pause(); + return subscription; + } + + /// Closes the group, indicating that no more streams will be added. + /// + /// If there are no streams in the group, [stream] is closed immediately. + /// Otherwise, [stream] will close once all streams in the group close. + /// + /// Returns a [Future] that completes once [stream] has actually been closed. + Future close() { + if (_closed) return _controller.done; + + _closed = true; + if (_subscriptions.isEmpty) _controller.close(); + + return _controller.done; + } +} + +/// An enum of possible states of a [StreamGroup]. +class _StreamGroupState { + /// The group has no listeners. + /// + /// New streams added to the group will be listened once the group has a + /// listener. + static const dormant = const _StreamGroupState("dormant"); + + /// The group has one or more listeners and is actively firing events. + /// + /// New streams added to the group will be immediately listeners. + static const listening = const _StreamGroupState("listening"); + + /// The group is paused and no more events will be fired until it resumes. + /// + /// New streams added to the group will be listened to, but then paused. They + /// will be resumed once the group itself is resumed. + /// + /// This state is only used by single-subscriber groups. + static const paused = const _StreamGroupState("paused"); + + /// The group is canceled and no more events will be fired ever. + /// + /// New streams added to the group will be listened to, canceled, and + /// discarded. + /// + /// This state is only used by single-subscriber groups. + static const canceled = const _StreamGroupState("canceled"); + + /// The name of the state. + /// + /// Used for debugging. + final String name; + + const _StreamGroupState(this.name); + + String toString() => name; +} diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart new file mode 100644 index 00000000..727077ce --- /dev/null +++ b/pkgs/async/test/stream_group_test.dart @@ -0,0 +1,724 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.test.stream_group_test; + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +main() { + group("single-subscription", () { + var streamGroup; + setUp(() { + streamGroup = new StreamGroup(); + }); + + test("buffers events from multiple sources", () async { + var controller1 = new StreamController(); + streamGroup.add(controller1.stream); + controller1.add("first"); + controller1.close(); + + var controller2 = new StreamController(); + streamGroup.add(controller2.stream); + controller2.add("second"); + controller2.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + + expect(streamGroup.stream.toList(), + completion(unorderedEquals(["first", "second"]))); + }); + + test("buffers errors from multiple sources", () async { + var controller1 = new StreamController(); + streamGroup.add(controller1.stream); + controller1.addError("first"); + controller1.close(); + + var controller2 = new StreamController(); + streamGroup.add(controller2.stream); + controller2.addError("second"); + controller2.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + + var transformed = streamGroup.stream.transform( + new StreamTransformer.fromHandlers( + handleError: (error, _, sink) => sink.add("error: $error"))); + expect(transformed.toList(), + completion(equals(["error: first", "error: second"]))); + }); + + test("buffers events and errors together", () async { + var controller = new StreamController(); + streamGroup.add(controller.stream); + + controller.add("first"); + controller.addError("second"); + controller.add("third"); + controller.addError("fourth"); + controller.addError("fifth"); + controller.add("sixth"); + controller.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + + var transformed = streamGroup.stream.transform( + new StreamTransformer.fromHandlers( + handleData: (data, sink) => sink.add("data: $data"), + handleError: (error, _, sink) => sink.add("error: $error"))); + expect(transformed.toList(), completion(equals([ + "data: first", + "error: second", + "data: third", + "error: fourth", + "error: fifth", + "data: sixth" + ]))); + }); + + test("emits events once there's a listener", () { + var controller = new StreamController(); + streamGroup.add(controller.stream); + + expect(streamGroup.stream.toList(), + completion(equals(["first", "second"]))); + + controller.add("first"); + controller.add("second"); + controller.close(); + + expect(streamGroup.close(), completes); + }); + + test("doesn't buffer events from a broadcast stream", () async { + var controller = new StreamController.broadcast(); + streamGroup.add(controller.stream); + + controller.add("first"); + controller.add("second"); + controller.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + expect(streamGroup.stream.toList(), completion(isEmpty)); + }); + + test("when paused, buffers events from a broadcast stream", () async { + var controller = new StreamController.broadcast(); + streamGroup.add(controller.stream); + + var events = []; + var subscription = streamGroup.stream.listen(events.add); + subscription.pause(); + + controller.add("first"); + controller.add("second"); + controller.close(); + await flushMicrotasks(); + + subscription.resume(); + expect(streamGroup.close(), completes); + await flushMicrotasks(); + + expect(events, equals(["first", "second"])); + }); + + test("emits events from a broadcast stream once there's a listener", () { + var controller = new StreamController.broadcast(); + streamGroup.add(controller.stream); + + expect(streamGroup.stream.toList(), + completion(equals(["first", "second"]))); + + controller.add("first"); + controller.add("second"); + controller.close(); + + expect(streamGroup.close(), completes); + }); + + test("forwards cancel errors", () async { + var subscription = streamGroup.stream.listen(null); + + var controller = new StreamController( + onCancel: () => throw "error"); + streamGroup.add(controller.stream); + await flushMicrotasks(); + + expect(subscription.cancel(), throwsA("error")); + }); + + test("forwards a cancel future", () async { + var subscription = streamGroup.stream.listen(null); + + var completer = new Completer(); + var controller = new StreamController( + onCancel: () => completer.future); + streamGroup.add(controller.stream); + await flushMicrotasks(); + + var fired = false; + subscription.cancel().then((_) => fired = true); + + await flushMicrotasks(); + expect(fired, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(fired, isTrue); + }); + + test("add() while active pauses the stream if the group is paused, then " + "resumes once the group resumes", () async { + var subscription = streamGroup.stream.listen(null); + await flushMicrotasks(); + + var paused = false; + var controller = new StreamController( + onPause: () => paused = true, + onResume: () => paused = false); + + subscription.pause(); + await flushMicrotasks(); + + streamGroup.add(controller.stream); + await flushMicrotasks(); + expect(paused, isTrue); + + subscription.resume(); + await flushMicrotasks(); + expect(paused, isFalse); + }); + + group("add() while canceled", () { + setUp(() async { + streamGroup.stream.listen(null).cancel(); + await flushMicrotasks(); + }); + + test("immediately listens to and cancels the stream", () async { + var listened = false; + var canceled = false; + var controller = new StreamController(onListen: () { + listened = true; + }, onCancel: expectAsync(() { + expect(listened, isTrue); + canceled = true; + })); + + streamGroup.add(controller.stream); + await flushMicrotasks(); + expect(listened, isTrue); + expect(canceled, isTrue); + }); + + test("forwards cancel errors", () { + var controller = new StreamController( + onCancel: () => throw "error"); + + expect(streamGroup.add(controller.stream), throwsA("error")); + }); + + test("forwards a cancel future", () async { + var completer = new Completer(); + var controller = new StreamController( + onCancel: () => completer.future); + + var fired = false; + streamGroup.add(controller.stream).then((_) => fired = true); + + await flushMicrotasks(); + expect(fired, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(fired, isTrue); + }); + }); + }); + + group("broadcast", () { + var streamGroup; + setUp(() { + streamGroup = new StreamGroup.broadcast(); + }); + + test("buffers events from multiple sources", () async { + var controller1 = new StreamController(); + streamGroup.add(controller1.stream); + controller1.add("first"); + controller1.close(); + + var controller2 = new StreamController(); + streamGroup.add(controller2.stream); + controller2.add("second"); + controller2.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + + expect(streamGroup.stream.toList(), + completion(equals(["first", "second"]))); + }); + + test("emits events from multiple sources once there's a listener", () { + var controller1 = new StreamController(); + streamGroup.add(controller1.stream); + + var controller2 = new StreamController(); + streamGroup.add(controller2.stream); + + expect(streamGroup.stream.toList(), + completion(equals(["first", "second"]))); + + controller1.add("first"); + controller2.add("second"); + controller1.close(); + controller2.close(); + + expect(streamGroup.close(), completes); + }); + + test("doesn't buffer events once a listener has been added and removed", + () async { + var controller = new StreamController(); + streamGroup.add(controller.stream); + + streamGroup.stream.listen(null).cancel(); + await flushMicrotasks(); + + controller.add("first"); + controller.addError("second"); + controller.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + expect(streamGroup.stream.toList(), completion(isEmpty)); + }); + + test("doesn't buffer events from a broadcast stream", () async { + var controller = new StreamController.broadcast(); + streamGroup.add(controller.stream); + controller.add("first"); + controller.addError("second"); + controller.close(); + + await flushMicrotasks(); + + expect(streamGroup.close(), completes); + expect(streamGroup.stream.toList(), completion(isEmpty)); + }); + + test("emits events from a broadcast stream once there's a listener", () { + var controller = new StreamController.broadcast(); + streamGroup.add(controller.stream); + + expect(streamGroup.stream.toList(), + completion(equals(["first", "second"]))); + + controller.add("first"); + controller.add("second"); + controller.close(); + + expect(streamGroup.close(), completes); + }); + + test("cancels and re-listens broadcast streams", () async { + var subscription = streamGroup.stream.listen(null); + + var controller = new StreamController.broadcast(); + + streamGroup.add(controller.stream); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + subscription.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + streamGroup.stream.listen(null); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + }); + + test("never cancels single-subscription streams", () async { + var subscription = streamGroup.stream.listen(null); + + var controller = new StreamController( + onCancel: expectAsync(() {}, count: 0)); + + streamGroup.add(controller.stream); + await flushMicrotasks(); + + subscription.cancel(); + await flushMicrotasks(); + + streamGroup.stream.listen(null); + await flushMicrotasks(); + }); + + test("drops events from a single-subscription stream while dormant", + () async { + var events = []; + var subscription = streamGroup.stream.listen(events.add); + + var controller = new StreamController(); + streamGroup.add(controller.stream); + await flushMicrotasks(); + + controller.add("first"); + await flushMicrotasks(); + expect(events, equals(["first"])); + + subscription.cancel(); + controller.add("second"); + await flushMicrotasks(); + expect(events, equals(["first"])); + + streamGroup.stream.listen(events.add); + controller.add("third"); + await flushMicrotasks(); + expect(events, equals(["first", "third"])); + }); + + test("a single-subscription stream can be removed while dormant", () async { + var controller = new StreamController(); + streamGroup.add(controller.stream); + await flushMicrotasks(); + + streamGroup.stream.listen(null).cancel(); + await flushMicrotasks(); + + streamGroup.remove(controller.stream); + expect(controller.hasListener, isFalse); + await flushMicrotasks(); + + expect(streamGroup.stream.toList(), completion(isEmpty)); + controller.add("first"); + expect(streamGroup.close(), completes); + }); + }); + + group("regardless of type", () { + group("single-subscription", () { + regardlessOfType(() => new StreamGroup()); + }); + + group("broadcast", () { + regardlessOfType(() => new StreamGroup.broadcast()); + }); + }); + + test("merge() emits events from all components streams", () { + var controller1 = new StreamController(); + var controller2 = new StreamController(); + + var merged = StreamGroup.merge([controller1.stream, controller2.stream]); + + controller1.add("first"); + controller1.close(); + controller2.add("second"); + controller2.close(); + + expect(merged.toList(), completion(unorderedEquals(["first", "second"]))); + }); +} + +void regardlessOfType(StreamGroup newStreamGroup()) { + var streamGroup; + setUp(() { + streamGroup = newStreamGroup(); + }); + + group("add()", () { + group("while dormant", () { + test("doesn't listen to the stream until the group is listened to", + () async { + var controller = new StreamController(); + + expect(streamGroup.add(controller.stream), isNull); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + streamGroup.stream.listen(null); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + }); + + test("is a no-op if the stream is already in the group", () { + var controller = new StreamController(); + streamGroup.add(controller.stream); + streamGroup.add(controller.stream); + streamGroup.add(controller.stream); + + // If the stream was actually listened to multiple times, this would + // throw a StateError. + streamGroup.stream.listen(null); + }); + }); + + group("while active", () { + var subscription; + setUp(() async { + subscription = streamGroup.stream.listen(null); + await flushMicrotasks(); + }); + + test("listens to the stream immediately", () async { + var controller = new StreamController(); + + expect(streamGroup.add(controller.stream), isNull); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + }); + + test("is a no-op if the stream is already in the group", () async { + var controller = new StreamController(); + + // If the stream were actually listened to more than once, future + // calls to [add] would throw [StateError]s. + streamGroup.add(controller.stream); + streamGroup.add(controller.stream); + streamGroup.add(controller.stream); + }); + }); + }); + + group("remove()", () { + group("while dormant", () { + test("stops emitting events for a stream that's removed", () async { + var controller = new StreamController(); + streamGroup.add(controller.stream); + + expect(streamGroup.stream.toList(), completion(equals(["first"]))); + + controller.add("first"); + await flushMicrotasks(); + controller.add("second"); + + expect(streamGroup.remove(controller.stream), isNull); + expect(streamGroup.close(), completes); + }); + + test("is a no-op for an unknown stream", () { + var controller = new StreamController(); + expect(streamGroup.remove(controller.stream), isNull); + }); + + test("and closed closes the group when the last stream is removed", + () async { + var controller1 = new StreamController(); + var controller2 = new StreamController(); + + streamGroup.add(controller1.stream); + streamGroup.add(controller2.stream); + await flushMicrotasks(); + + streamGroup.close(); + + streamGroup.remove(controller1.stream); + await flushMicrotasks(); + + streamGroup.remove(controller2.stream); + await flushMicrotasks(); + + expect(streamGroup.stream.toList(), completion(isEmpty)); + }); + }); + + group("while listening", () { + test("doesn't emit events from a removed stream", () { + var controller = new StreamController(); + streamGroup.add(controller.stream); + + // The subscription to [controller.stream] is canceled synchronously, so + // the first event is dropped even though it was added before the + // removal. This is documented in [StreamGroup.remove]. + expect(streamGroup.stream.toList(), completion(isEmpty)); + + controller.add("first"); + expect(streamGroup.remove(controller.stream), isNull); + controller.add("second"); + + expect(streamGroup.close(), completes); + }); + + test("cancels the stream's subscription", () async { + var controller = new StreamController(); + streamGroup.add(controller.stream); + + streamGroup.stream.listen(null); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + streamGroup.remove(controller.stream); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + }); + + test("forwards cancel errors", () async { + var controller = new StreamController( + onCancel: () => throw "error"); + streamGroup.add(controller.stream); + + streamGroup.stream.listen(null); + await flushMicrotasks(); + + expect(streamGroup.remove(controller.stream), throwsA("error")); + }); + + test("forwards cancel futures", () async { + var completer = new Completer(); + var controller = new StreamController( + onCancel: () => completer.future); + + streamGroup.stream.listen(null); + await flushMicrotasks(); + + streamGroup.add(controller.stream); + await flushMicrotasks(); + + var fired = false; + streamGroup.remove(controller.stream).then((_) => fired = true); + + await flushMicrotasks(); + expect(fired, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(fired, isTrue); + }); + + test("is a no-op for an unknown stream", () async { + var controller = new StreamController(); + streamGroup.stream.listen(null); + await flushMicrotasks(); + + expect(streamGroup.remove(controller.stream), isNull); + }); + + test("and closed closes the group when the last stream is removed", + () async { + var done = false; + streamGroup.stream.listen(null, onDone: () => done = true); + await flushMicrotasks(); + + var controller1 = new StreamController(); + var controller2 = new StreamController(); + + streamGroup.add(controller1.stream); + streamGroup.add(controller2.stream); + await flushMicrotasks(); + + streamGroup.close(); + + streamGroup.remove(controller1.stream); + await flushMicrotasks(); + expect(done, isFalse); + + streamGroup.remove(controller2.stream); + await flushMicrotasks(); + expect(done, isTrue); + }); + }); + }); + + group("close()", () { + group("while dormant", () { + test("if there are no streams, closes the group", () { + expect(streamGroup.close(), completes); + expect(streamGroup.stream.toList(), completion(isEmpty)); + }); + + test("if there are streams, closes the group once those streams close " + "and there's a listener", () async { + var controller1 = new StreamController(); + var controller2 = new StreamController(); + + streamGroup.add(controller1.stream); + streamGroup.add(controller2.stream); + await flushMicrotasks(); + + streamGroup.close(); + + controller1.close(); + controller2.close(); + expect(streamGroup.stream.toList(), completion(isEmpty)); + }); + }); + + group("while active", () { + test("if there are no streams, closes the group", () { + expect(streamGroup.stream.toList(), completion(isEmpty)); + expect(streamGroup.close(), completes); + }); + + test("if there are streams, closes the group once those streams close", + () async { + var done = false; + streamGroup.stream.listen(null, onDone: () => done = true); + await flushMicrotasks(); + + var controller1 = new StreamController(); + var controller2 = new StreamController(); + + streamGroup.add(controller1.stream); + streamGroup.add(controller2.stream); + await flushMicrotasks(); + + streamGroup.close(); + await flushMicrotasks(); + expect(done, isFalse); + + controller1.close(); + await flushMicrotasks(); + expect(done, isFalse); + + controller2.close(); + await flushMicrotasks(); + expect(done, isTrue); + }); + }); + + test("returns a Future that completes once all events are dispatched", + () async { + var events = []; + streamGroup.stream.listen(events.add); + + var controller = new StreamController(); + streamGroup.add(controller.stream); + await flushMicrotasks(); + + // Add a bunch of events. Each of these will get dispatched in a + // separate microtask, so we can test that [close] only completes once + // all of them have dispatched. + controller.add("one"); + controller.add("two"); + controller.add("three"); + controller.add("four"); + controller.add("five"); + controller.add("six"); + controller.close(); + + await streamGroup.close(); + expect(events, equals(["one", "two", "three", "four", "five", "six"])); + }); + }); +} + +/// Wait for all microtasks to complete. +Future flushMicrotasks() => new Future.delayed(Duration.ZERO); From 16755c5145f1c5b73f8d1460ceb668169451044f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 23 Jun 2015 14:03:24 -0700 Subject: [PATCH 021/260] Add a StreamSplitter class. This splits a stream into multiple identical streams. R=lrn@google.com Review URL: https://codereview.chromium.org//1190333007. --- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/stream_splitter.dart | 213 ++++++++++++++++ pkgs/async/test/stream_splitter_test.dart | 288 ++++++++++++++++++++++ 3 files changed, 502 insertions(+) create mode 100644 pkgs/async/lib/src/stream_splitter.dart create mode 100644 pkgs/async/test/stream_splitter_test.dart diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 345b8dcf..a7ff26c2 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -6,5 +6,6 @@ library dart.pkg.async; export "src/future_group.dart"; export "src/stream_group.dart"; +export "src/stream_splitter.dart"; export "stream_zip.dart"; export "result.dart"; diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart new file mode 100644 index 00000000..addba934 --- /dev/null +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -0,0 +1,213 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_splitter; + +import 'dart:async'; +import 'dart:collection'; + +import '../result.dart'; +import 'future_group.dart'; + +/// A class that splits a single source stream into an arbitrary number of +/// (single-subscription) streams (called "branch") that emit the same events. +/// +/// Each branch will emit all the same values and errors as the source stream, +/// regardless of which values have been emitted on other branches. This means +/// that the splitter stores every event that has been emitted so far, which may +/// consume a lot of memory. The user can call [close] to indicate that no more +/// branches will be created, and this memory will be released. +/// +/// The source stream is only listened to once a branch is created *and listened +/// to*. It's paused when all branches are paused *or when all branches are +/// canceled*, and resumed once there's at least one branch that's listening and +/// unpaused. It's not canceled unless no branches are listening and [close] has +/// been called. +class StreamSplitter { + /// The wrapped stream. + final Stream _stream; + + /// The subscription to [_stream]. + /// + /// This will be `null` until a branch has a listener. + StreamSubscription _subscription; + + /// The buffer of events or errors that have already been emitted by + /// [_stream]. + final _buffer = new Queue>(); + + /// The controllers for branches that are listening for future events from + /// [_stream]. + /// + /// Once a branch is canceled, it's removed from this list. When [_stream] is + /// done, all branches are removed. + final _controllers = new Set>(); + + /// A group of futures returned by [close]. + /// + /// This is used to ensure that [close] doesn't complete until all + /// [StreamController.close] and [StreamSubscription.cancel] calls complete. + final _closeGroup = new FutureGroup(); + + /// Whether [_stream] is done emitting events. + var _isDone = false; + + /// Whether [close] has been called. + var _isClosed = false; + + /// Splits [stream] into [count] identical streams. + /// + /// [count] defaults to 2. This is the same as creating [count] branches and + /// then closing the [StreamSplitter]. + static List splitFrom(Stream stream, [int count]) { + if (count == null) count = 2; + var splitter = new StreamSplitter(stream); + var streams = new List.generate(count, (_) => splitter.split()); + splitter.close(); + return streams; + } + + StreamSplitter(this._stream); + + /// Returns a single-subscription stream that's a copy of the input stream. + /// + /// This will throw a [StateError] if [close] has been called. + Stream split() { + if (_isClosed) { + throw new StateError("Can't call split() on a closed StreamSplitter."); + } + + var controller; + controller = new StreamController( + onListen: _onListen, + onPause: _onPause, + onResume: _onResume, + onCancel: () => _onCancel(controller)); + + for (var result in _buffer) { + result.addTo(controller); + } + + if (_isDone) { + _closeGroup.add(controller.close()); + } else { + _controllers.add(controller); + } + + return controller.stream; + } + + /// Indicates that no more branches will be requested via [split]. + /// + /// This clears the internal buffer of events. If there are no branches or all + /// branches have been canceled, this cancels the subscription to the input + /// stream. + /// + /// Returns a [Future] that completes once all events have been processed by + /// all branches and (if applicable) the subscription to the input stream has + /// been canceled. + Future close() { + if (_isClosed) return _closeGroup.future; + _isClosed = true; + + _buffer.clear(); + if (_controllers.isEmpty) _cancelSubscription(); + + return _closeGroup.future; + } + + /// Cancel [_subscription] and close [_closeGroup]. + /// + /// This should be called after all the branches' subscriptions have been + /// canceled and the splitter has been closed. In that case, we won't use the + /// events from [_subscription] any more, since there's nothing to pipe them + /// to and no more branches will be created. If [_subscription] is done, + /// canceling it will be a no-op. + /// + /// This may also be called before any branches have been created, in which + /// case [_subscription] will be `null`. + void _cancelSubscription() { + assert(_controllers.isEmpty); + assert(_isClosed); + + var future = null; + if (_subscription != null) future = _subscription.cancel(); + if (future != null) _closeGroup.add(future); + _closeGroup.close(); + } + + // StreamController events + + /// Subscribe to [_stream] if we haven't yet done so, and resume the + /// subscription if we have. + void _onListen() { + if (_isDone) return; + + if (_subscription != null) { + // Resume the subscription in case it was paused, either because all the + // controllers were paused or because the last one was canceled. If it + // wasn't paused, this will be a no-op. + _subscription.resume(); + } else { + _subscription = _stream.listen( + _onData, onError: _onError, onDone: _onDone); + } + } + + /// Pauses [_subscription] if every controller is paused. + void _onPause() { + if (!_controllers.every((controller) => controller.isPaused)) return; + _subscription.pause(); + } + + /// Resumes [_subscription]. + /// + /// If [_subscription] wasn't paused, this is a no-op. + void _onResume() { + _subscription.resume(); + } + + /// Removes [controller] from [_controllers] and cancels or pauses + /// [_subscription] as appropriate. + /// + /// Since the controller emitting a done event will cause it to register as + /// canceled, this is the only way that a controller is ever removed from + /// [_controllers]. + void _onCancel(StreamController controller) { + _controllers.remove(controller); + if (_controllers.isNotEmpty) return; + + if (_isClosed) { + _cancelSubscription(); + } else { + _subscription.pause(); + } + } + + // Stream events + + /// Buffers [data] and passes it to [_controllers]. + void _onData(T data) { + if (!_isClosed) _buffer.add(new Result.value(data)); + for (var controller in _controllers) { + controller.add(data); + } + } + + /// Buffers [error] and passes it to [_controllers]. + void _onError(Object error, StackTrace stackTrace) { + if (!_isClosed) _buffer.add(new Result.error(error, stackTrace)); + for (var controller in _controllers) { + controller.addError(error, stackTrace); + } + } + + /// Marks [_controllers] as done. + void _onDone() { + _isDone = true; + for (var controller in _controllers) { + _closeGroup.add(controller.close()); + } + } +} diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart new file mode 100644 index 00000000..7b77baa7 --- /dev/null +++ b/pkgs/async/test/stream_splitter_test.dart @@ -0,0 +1,288 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +main() { + var controller; + var splitter; + setUp(() { + controller = new StreamController(); + splitter = new StreamSplitter(controller.stream); + }); + + test("a branch that's created before the stream starts to replay it", + () async { + var events = []; + var branch = splitter.split(); + splitter.close(); + branch.listen(events.add); + + controller.add(1); + await flushMicrotasks(); + expect(events, equals([1])); + + controller.add(2); + await flushMicrotasks(); + expect(events, equals([1, 2])); + + controller.add(3); + await flushMicrotasks(); + expect(events, equals([1, 2, 3])); + + controller.close(); + }); + + test("a branch replays error events as well as data events", () { + var branch = splitter.split(); + splitter.close(); + + controller.add(1); + controller.addError("error"); + controller.add(3); + controller.close(); + + var count = 0; + branch.listen(expectAsync((value) { + expect(count, anyOf(0, 2)); + expect(value, equals(count + 1)); + count++; + }, count: 2), onError: expectAsync((error) { + expect(count, equals(1)); + expect(error, equals("error")); + count++; + }), onDone: expectAsync(() { + expect(count, equals(3)); + })); + }); + + test("a branch that's created in the middle of a stream replays it", () async { + controller.add(1); + controller.add(2); + await flushMicrotasks(); + + var branch = splitter.split(); + splitter.close(); + + controller.add(3); + controller.add(4); + controller.close(); + + expect(branch.toList(), completion(equals([1, 2, 3, 4]))); + }); + + test("a branch that's created after the stream is finished replays it", + () async { + controller.add(1); + controller.add(2); + controller.add(3); + controller.close(); + await flushMicrotasks(); + + expect(splitter.split().toList(), completion(equals([1, 2, 3]))); + splitter.close(); + }); + + test("creates single-subscription branches", () async { + var branch = splitter.split(); + expect(branch.isBroadcast, isFalse); + branch.listen(null); + expect(() => branch.listen(null), throwsStateError); + expect(() => branch.listen(null), throwsStateError); + }); + + test("creates branches with the correct reified type", () async { + var branch = splitter.split(); + expect(branch, new isInstanceOf>()); + expect(branch, isNot(new isInstanceOf>())); + }); + + test("multiple branches each replay the stream", () async { + var branch1 = splitter.split(); + controller.add(1); + controller.add(2); + await flushMicrotasks(); + + var branch2 = splitter.split(); + controller.add(3); + controller.close(); + await flushMicrotasks(); + + var branch3 = splitter.split(); + splitter.close(); + + expect(branch1.toList(), completion(equals([1, 2, 3]))); + expect(branch2.toList(), completion(equals([1, 2, 3]))); + expect(branch3.toList(), completion(equals([1, 2, 3]))); + }); + + test("a branch doesn't close until the source stream closes", () async { + var branch = splitter.split(); + splitter.close(); + + var closed = false; + branch.last.then((_) => closed = true); + + controller.add(1); + controller.add(2); + controller.add(3); + await flushMicrotasks(); + expect(closed, isFalse); + + controller.close(); + await flushMicrotasks(); + expect(closed, isTrue); + }); + + test("the source stream isn't listened to until a branch is", () async { + expect(controller.hasListener, isFalse); + + var branch = splitter.split(); + splitter.close(); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + branch.listen(null); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + }); + + test("the source stream is paused when all branches are paused", () async { + var branch1 = splitter.split(); + var branch2 = splitter.split(); + var branch3 = splitter.split(); + splitter.close(); + + var subscription1 = branch1.listen(null); + var subscription2 = branch2.listen(null); + var subscription3 = branch3.listen(null); + + subscription1.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + subscription2.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + subscription3.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + subscription2.resume(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + }); + + test("the source stream is paused when all branches are canceled", () async { + var branch1 = splitter.split(); + var branch2 = splitter.split(); + var branch3 = splitter.split(); + + var subscription1 = branch1.listen(null); + var subscription2 = branch2.listen(null); + var subscription3 = branch3.listen(null); + + subscription1.cancel(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + subscription2.cancel(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + subscription3.cancel(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + var branch4 = splitter.split(); + splitter.close(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + branch4.listen(null); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + }); + + test("the source stream is canceled when it's closed after all branches have " + "been canceled", () async { + var branch1 = splitter.split(); + var branch2 = splitter.split(); + var branch3 = splitter.split(); + + var subscription1 = branch1.listen(null); + var subscription2 = branch2.listen(null); + var subscription3 = branch3.listen(null); + + subscription1.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + subscription2.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + subscription3.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + splitter.close(); + expect(controller.hasListener, isFalse); + }); + + test("the source stream is canceled when all branches are canceled after it " + "has been closed", () async { + var branch1 = splitter.split(); + var branch2 = splitter.split(); + var branch3 = splitter.split(); + splitter.close(); + + var subscription1 = branch1.listen(null); + var subscription2 = branch2.listen(null); + var subscription3 = branch3.listen(null); + + subscription1.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + subscription2.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + subscription3.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + }); + + test("a splitter that's closed before any branches are added never listens " + "to the source stream", () { + splitter.close(); + + // This would throw an error if the stream had already been listened to. + controller.stream.listen(null); + }); + + test("splitFrom splits a source stream into the designated number of " + "branches", () { + var branches = StreamSplitter.splitFrom(controller.stream, 5); + + controller.add(1); + controller.add(2); + controller.add(3); + controller.close(); + + expect(branches[0].toList(), completion(equals([1, 2, 3]))); + expect(branches[1].toList(), completion(equals([1, 2, 3]))); + expect(branches[2].toList(), completion(equals([1, 2, 3]))); + expect(branches[3].toList(), completion(equals([1, 2, 3]))); + expect(branches[4].toList(), completion(equals([1, 2, 3]))); + }); +} + +/// Wait for all microtasks to complete. +Future flushMicrotasks() => new Future.delayed(Duration.ZERO); From 380cb201f9742469166200af8003f931e8d6a1b4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 23 Jun 2015 14:18:22 -0700 Subject: [PATCH 022/260] Remove the test for reified StreamSplitter types. This didn't pass on 1.10 and earlier, since StreamController exposed a non-reified stream. TBR Review URL: https://codereview.chromium.org//1206663002. --- pkgs/async/test/stream_splitter_test.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 7b77baa7..ffd0c87a 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -95,11 +95,9 @@ main() { expect(() => branch.listen(null), throwsStateError); }); - test("creates branches with the correct reified type", () async { - var branch = splitter.split(); - expect(branch, new isInstanceOf>()); - expect(branch, isNot(new isInstanceOf>())); - }); + // TODO(nweiz): Test that branches have the correct reified type once Dart + // 1.11 is released. In 1.10, the stream exposed by a StreamController didn't + // have a reified type. test("multiple branches each replay the stream", () async { var branch1 = splitter.split(); From 063fb26bc8773842cf15b3ebdd6b962fffeb5bd9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 25 Jun 2015 18:29:56 -0700 Subject: [PATCH 023/260] Release 1.2.0. Review URL: https://codereview.chromium.org//1212223002. --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 102f2d44..1d8d181c 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.2.0-dev +version: 1.2.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 95092e39a030a965c070bc568538c8189dd4ba45 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 30 Jun 2015 18:10:31 -0700 Subject: [PATCH 024/260] cleanup unused variables in tests R=nweiz@google.com Review URL: https://codereview.chromium.org//1216313003. --- pkgs/async/test/stream_group_test.dart | 3 +-- pkgs/async/test/stream_zip_test.dart | 8 ++++++-- pkgs/async/test/stream_zip_zone_test.dart | 1 - 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 727077ce..db6c9387 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -472,9 +472,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); group("while active", () { - var subscription; setUp(() async { - subscription = streamGroup.stream.listen(null); + streamGroup.stream.listen(null); await flushMicrotasks(); }); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 9606b924..35ace7d1 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -155,8 +155,6 @@ main() { }); test("Pause/Resume", () { - var done = expectAsync((){}); // Call to complete test. - int sc1p = 0; StreamController c1 = new StreamController( onPause: () { @@ -174,6 +172,12 @@ main() { onResume: () { sc2p--; }); + + var done = expectAsync((){ + expect(sc1p, equals(1)); + expect(sc2p, equals(0)); + }); // Call to complete test. + Stream zip = new StreamZip([c1.stream, c2.stream]); const ms25 = const Duration(milliseconds: 25); diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index cdf2e164..47957a94 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -41,7 +41,6 @@ void testStream(String name, StreamController controller, Stream stream) { expect(v, 37); expect(Zone.current, newZone1); runZoned(() { - Zone newZone2 = Zone.current; sub.onData(expectAsync((v) { expect(v, 87); expect(Zone.current, newZone1); From fb93ae6b4a0c81daac9c7fe2d457a0a0e7beba52 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 30 Jun 2015 18:22:27 -0700 Subject: [PATCH 025/260] fix spelling in doc comments R=nweiz@google.com Review URL: https://codereview.chromium.org//1208123004. --- pkgs/async/lib/result.dart | 2 +- pkgs/async/lib/src/stream_group.dart | 4 ++-- pkgs/async/pubspec.yaml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart index db04ae71..c734a37a 100644 --- a/pkgs/async/lib/result.dart +++ b/pkgs/async/lib/result.dart @@ -97,7 +97,7 @@ abstract class Result { * Release a stream of [result] values into a stream of the results. * * `Result` values of the source stream become value or error events in - * the retuned stream as appropriate. + * the returned stream as appropriate. * Errors from the source stream become errors in the returned stream. * * Shorthand for transforming the stream using [ReleaseStreamTransformer]. diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 01477878..d99f5151 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -14,11 +14,11 @@ import 'dart:async'; /// this means that events emitted by broadcast streams will be dropped until /// [stream] has a listener.** /// -/// If the `StreamGroup` is construced using [new StreamGroup], [stream] will be +/// If the `StreamGroup` is constructed using [new StreamGroup], [stream] will be /// single-subscription. In this case, if [stream] is paused or canceled, all /// streams in the group will likewise be paused or canceled, respectively. /// -/// If the `StreamGroup` is construced using [new StreamGroup.broadcast], +/// If the `StreamGroup` is constructed using [new StreamGroup.broadcast], /// [stream] will be a broadcast stream. In this case, the streams in the group /// will never be paused and single-subscription streams in the group will never /// be canceled. **Note that single-subscription streams in a broadcast group diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 1d8d181c..e19f5003 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,10 +1,10 @@ name: async -version: 1.2.0 +version: 1.2.1-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async dev_dependencies: - test: "^0.12.0" stack_trace: "^1.0.0" + test: "^0.12.0" environment: - sdk: ">=1.9.0-dev.8.4 <2.0.0" + sdk: ">=1.9.0 <2.0.0" From b41b9cdc67aa8e26f1cbd6c39cf436483893bade Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 1 Jul 2015 10:43:31 +0200 Subject: [PATCH 026/260] Add new features to package:async. - `DelegatingStreamSubscription`: Wrap a subscription and maybe override some methods. - `SubscriptionStream`: Rewrap a stream subscription as a single-subscription stream. - `StreamCompleter`: Utility to help when avoiding returning a `Future`. - `StreamEvents`: A pull interface for reading the events of a stream. R=nweiz@google.com, sgjesse@google.com Review URL: https://codereview.chromium.org//1149563010. --- pkgs/async/CHANGELOG.md | 16 + pkgs/async/README.md | 3 +- pkgs/async/lib/async.dart | 8 +- .../src/delegating_stream_subscription.dart | 44 ++ pkgs/async/lib/src/stream_completer.dart | 180 +++++ pkgs/async/lib/src/stream_queue.dart | 619 +++++++++++++++++ pkgs/async/lib/src/subscription_stream.dart | 101 +++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_completer_test.dart | 371 ++++++++++ pkgs/async/test/stream_queue_test.dart | 632 ++++++++++++++++++ pkgs/async/test/subscription_stream_test.dart | 176 +++++ pkgs/async/test/utils.dart | 25 + 12 files changed, 2173 insertions(+), 4 deletions(-) create mode 100644 pkgs/async/lib/src/delegating_stream_subscription.dart create mode 100644 pkgs/async/lib/src/stream_completer.dart create mode 100644 pkgs/async/lib/src/stream_queue.dart create mode 100644 pkgs/async/lib/src/subscription_stream.dart create mode 100644 pkgs/async/test/stream_completer_test.dart create mode 100644 pkgs/async/test/stream_queue_test.dart create mode 100644 pkgs/async/test/subscription_stream_test.dart create mode 100644 pkgs/async/test/utils.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1333fe37..ab118014 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,19 @@ +## 1.3.0 + +- Added `StreamCompleter` class for creating a stream now and providing its + events later as another stream. + +- Added `StreamQueue` class which allows requesting events from a stream + before they are avilable. It is like a `StreamIterator` that can queue + requests. + +- Added `DelegatingStreamSubscription` which is a simple wrapper around + a `StreamSubscription` that forwards all call to the wrapped subscription. + It can be extended to wrap extra functionality around a subscription. + +- Added `SubscriptionStream` which creates a single-subscription stream + from an existing stream subscription. + ## 1.2.0 - Added a `FutureGroup` class for waiting for a group of futures, potentially of diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 55bea7f3..3820f064 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -2,7 +2,8 @@ Contains tools to work with asynchronous computations. -The package contains sub-libraries with different utilities. +The package contains `Stream` and `Future` related functionality, +as well as sub-libraries with different utilities. ### Zipping streams diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index a7ff26c2..fc36d435 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -4,8 +4,12 @@ library dart.pkg.async; +export "stream_zip.dart"; +export "result.dart"; +export "src/delegating_stream_subscription.dart"; export "src/future_group.dart"; +export "src/stream_completer.dart"; export "src/stream_group.dart"; +export "src/stream_queue.dart"; export "src/stream_splitter.dart"; -export "stream_zip.dart"; -export "result.dart"; +export "src/subscription_stream.dart"; diff --git a/pkgs/async/lib/src/delegating_stream_subscription.dart b/pkgs/async/lib/src/delegating_stream_subscription.dart new file mode 100644 index 00000000..87629e5a --- /dev/null +++ b/pkgs/async/lib/src/delegating_stream_subscription.dart @@ -0,0 +1,44 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.delegating_stream_subscription; + +import 'dart:async'; + +/// Simple delegating wrapper around a [StreamSubscription]. +/// +/// Subclasses can override individual methods. +class DelegatingStreamSubscription implements StreamSubscription { + final StreamSubscription _source; + + /// Create delegating subscription forwarding calls to [sourceSubscription]. + DelegatingStreamSubscription(StreamSubscription sourceSubscription) + : _source = sourceSubscription; + + void onData(void handleData(T data)) { + _source.onData(handleData); + } + + void onError(Function handleError) { + _source.onError(handleError); + } + + void onDone(void handleDone()) { + _source.onDone(handleDone); + } + + void pause([Future resumeFuture]) { + _source.pause(resumeFuture); + } + + void resume() { + _source.resume(); + } + + Future cancel() => _source.cancel(); + + Future asFuture([futureValue]) => _source.asFuture(futureValue); + + bool get isPaused => _source.isPaused; +} diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart new file mode 100644 index 00000000..c343e6e7 --- /dev/null +++ b/pkgs/async/lib/src/stream_completer.dart @@ -0,0 +1,180 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_completer; + +import "dart:async"; + +/// A single-subscription [stream] where the contents are provided later. +/// +/// It is generally recommended that you never create a `Future` +/// because you can just directly create a stream that doesn't do anything +/// until it's ready to do so. +/// This class can be used to create such a stream. +/// +/// The [stream] is a normal stream that you can listen to immediately, +/// but until either [setSourceStream] or [setEmpty] is called, +/// the stream won't produce any events. +/// +/// The same effect can be achieved by using a [StreamController] +/// and adding the stream using `addStream` when both +/// the controller's stream is listened to and the source stream is ready. +/// This class attempts to shortcut some of the overhead when possible. +/// For example, if the [stream] is only listened to +/// after the source stream has been set, +/// the listen is performed directly on the source stream. +class StreamCompleter { + /// The stream doing the actual work, is returned by [stream]. + final _CompleterStream _stream = new _CompleterStream(); + + /// Convert a `Future` to a `Stream`. + /// + /// This creates a stream using a stream completer, + /// and sets the source stream to the result of the future when the + /// future completes. + /// + /// If the future completes with an error, the returned stream will + /// instead contain just that error. + static Stream fromFuture(Future streamFuture) { + var completer = new StreamCompleter(); + streamFuture.then(completer.setSourceStream, + onError: (e, s) { + completer.setSourceStream(streamFuture.asStream()); + }); + return completer.stream; + } + + /// The stream of this completer. + /// + /// This stream is always a single-subscription stream. + /// + /// When a source stream is provided, its events will be forwarded to + /// listeners on this stream. + /// + /// The stream can be listened either before or after a source stream + /// is set. + Stream get stream => _stream; + + /// Set a stream as the source of events for the [StreamCompleter]'s + /// [stream]. + /// + /// The completer's `stream` will act exactly as [sourceStream]. + /// + /// If the source stream is set before [stream] is listened to, + /// the listen call on [stream] is forwarded directly to [sourceStream]. + /// + /// If [stream] is listened to before setting the source stream, + /// an intermediate subscription is created. It looks like a completely + /// normal subscription, and can be paused or canceled, but it won't + /// produce any events until a source stream is provided. + /// + /// If the `stream` subscription is canceled before a source stream is set, + /// the source stream will be listened to and immediately canceled again. + /// + /// Otherwise, when the source stream is then set, + /// it is immediately listened to, and its events are forwarded to the + /// existing subscription. + /// + /// Either [setSourceStream] or [setEmpty] may be called at most once. + /// Trying to call either of them again will fail. + void setSourceStream(Stream sourceStream) { + if (_stream._isSourceStreamSet) { + throw new StateError("Source stream already set"); + } + _stream._setSourceStream(sourceStream); + } + + /// Equivalent to setting an empty stream using [setSourceStream]. + /// + /// Either [setSourceStream] or [setEmpty] may be called at most once. + /// Trying to call either of them again will fail. + void setEmpty() { + if (_stream._isSourceStreamSet) { + throw new StateError("Source stream already set"); + } + _stream._setEmpty(); + } +} + +/// Stream completed by [StreamCompleter]. +class _CompleterStream extends Stream { + /// Controller for an intermediate stream. + /// + /// Created if the user listens on this stream before the source stream + /// is set, or if using [_setEmpty] so there is no source stream. + StreamController _controller; + + /// Source stream for the events provided by this stream. + /// + /// Set when the completer sets the source stream using [_setSourceStream] + /// or [_setEmpty]. + Stream _sourceStream; + + StreamSubscription listen(onData(T data), + {Function onError, + void onDone(), + bool cancelOnError}) { + if (_controller == null) { + if (_sourceStream != null && !_sourceStream.isBroadcast) { + // If the source stream is itself single subscription, + // just listen to it directly instead of creating a controller. + return _sourceStream.listen(onData, onError: onError, onDone: onDone, + cancelOnError: cancelOnError); + } + _createController(); + if (_sourceStream != null) { + _linkStreamToController(); + } + } + return _controller.stream.listen(onData, onError: onError, onDone: onDone, + cancelOnError: cancelOnError); + } + + /// Whether a source stream has been set. + /// + /// Used to throw an error if trying to set a source stream twice. + bool get _isSourceStreamSet => _sourceStream != null; + + /// Sets the source stream providing the events for this stream. + /// + /// If set before the user listens, listen calls will be directed directly + /// to the source stream. If the user listenes earlier, and intermediate + /// stream is created using a stream controller, and the source stream is + /// linked into that stream later. + void _setSourceStream(Stream sourceStream) { + assert(_sourceStream == null); + _sourceStream = sourceStream; + if (_controller != null) { + // User has already listened, so provide the data through controller. + _linkStreamToController(); + } + } + + /// Links source stream to controller when both are available. + void _linkStreamToController() { + assert(_controller != null); + assert(_sourceStream != null); + _controller.addStream(_sourceStream, cancelOnError: false) + .whenComplete(_controller.close); + } + + /// Sets an empty source stream. + /// + /// Uses [_controller] for the stream, then closes the controller + /// immediately. + void _setEmpty() { + assert(_sourceStream == null); + if (_controller == null) { + _createController(); + } + _sourceStream = _controller.stream; // Mark stream as set. + _controller.close(); + } + + // Creates the [_controller]. + void _createController() { + assert(_controller == null); + _controller = new StreamController(sync: true); + } +} diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart new file mode 100644 index 00000000..5255b044 --- /dev/null +++ b/pkgs/async/lib/src/stream_queue.dart @@ -0,0 +1,619 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_events; + +import 'dart:async'; +import 'dart:collection'; + +import "subscription_stream.dart"; +import "stream_completer.dart"; +import "../result.dart"; + +/// An asynchronous pull-based interface for accessing stream events. +/// +/// Wraps a stream and makes individual events available on request. +/// +/// You can request (and reserve) one or more events from the stream, +/// and after all previous requests have been fulfilled, stream events +/// go towards fulfilling your request. +/// +/// For example, if you ask for [next] two times, the returned futures +/// will be completed by the next two unrequested events from the stream. +/// +/// The stream subscription is paused when there are no active +/// requests. +/// +/// Some streams, including broadcast streams, will buffer +/// events while paused, so waiting too long between requests may +/// cause memory bloat somewhere else. +/// +/// This is similar to, but more convenient than, a [StreamIterator]. +/// A `StreamIterator` requires you to manually check when a new event is +/// available and you can only access the value of that event until you +/// check for the next one. A `StreamQueue` allows you to request, for example, +/// three events at a time, either individually, as a group using [take] +/// or [skip], or in any combination. +/// +/// You can also ask to have the [rest] of the stream provided as +/// a new stream. This allows, for example, taking the first event +/// out of a stream and continuing to use the rest of the stream as a stream. +/// +/// Example: +/// +/// var events = new StreamQueue(someStreamOfLines); +/// var first = await events.next; +/// while (first.startsWith('#')) { +/// // Skip comments. +/// first = await events.next; +/// } +/// +/// if (first.startsWith(MAGIC_MARKER)) { +/// var headerCount = +/// first.parseInt(first.substring(MAGIC_MARKER.length + 1)); +/// handleMessage(headers: await events.take(headerCount), +/// body: events.rest); +/// return; +/// } +/// // Error handling. +/// +/// When you need no further events the `StreamQueue` should be closed +/// using [cancel]. This releases the underlying stream subscription. +class StreamQueue { + // This class maintains two queues: one of events and one of requests. + // The active request (the one in front of the queue) is called with + // the current event queue when it becomes active. + // + // If the request returns true, it's complete and will be removed from the + // request queue. + // If the request returns false, it needs more events, and will be called + // again when new events are available. + // The request can remove events that it uses, or keep them in the event + // queue until it has all that it needs. + // + // This model is very flexible and easily extensible. + // It allows requests that don't consume events (like [hasNext]) or + // potentially a request that takes either five or zero events, determined + // by the content of the fifth event. + + /// Source of events. + final Stream _sourceStream; + + /// Subscription on [_sourceStream] while listening for events. + /// + /// Set to subscription when listening, and set to `null` when the + /// subscription is done (and [_isDone] is set to true). + StreamSubscription _subscription; + + /// Whether we have listened on [_sourceStream] and the subscription is done. + bool _isDone = false; + + /// Whether a closing operation has been performed on the stream queue. + /// + /// Closing operations are [cancel] and [rest]. + bool _isClosed = false; + + /// Queue of events not used by a request yet. + final Queue _eventQueue = new Queue(); + + /// Queue of pending requests. + /// + /// Access through methods below to ensure consistency. + final Queue<_EventRequest> _requestQueue = new Queue(); + + /// Create a `StreamQueue` of the events of [source]. + StreamQueue(Stream source) + : _sourceStream = source; + + /// Asks if the stream has any more events. + /// + /// Returns a future that completes with `true` if the stream has any + /// more events, whether data or error. + /// If the stream closes without producing any more events, the returned + /// future completes with `false`. + /// + /// Can be used before using [next] to avoid getting an error in the + /// future returned by `next` in the case where there are no more events. + Future get hasNext { + if (!_isClosed) { + var hasNextRequest = new _HasNextRequest(); + _addRequest(hasNextRequest); + return hasNextRequest.future; + } + throw _failClosed(); + } + + /// Requests the next (yet unrequested) event from the stream. + /// + /// When the requested event arrives, the returned future is completed with + /// the event. + /// If the event is a data event, the returned future completes + /// with its value. + /// If the event is an error event, the returned future completes with + /// its error and stack trace. + /// If the stream closes before an event arrives, the returned future + /// completes with a [StateError]. + /// + /// It's possible to have several pending [next] calls (or other requests), + /// and they will be completed in the order they were requested, by the + /// first events that were not consumed by previous requeusts. + Future get next { + if (!_isClosed) { + var nextRequest = new _NextRequest(); + _addRequest(nextRequest); + return nextRequest.future; + } + throw _failClosed(); + } + + /// Returns a stream of all the remaning events of the source stream. + /// + /// All requested [next], [skip] or [take] operations are completed + /// first, and then any remaining events are provided as events of + /// the returned stream. + /// + /// Using `rest` closes this stream queue. After getting the + /// `rest` the caller may no longer request other events, like + /// after calling [cancel]. + Stream get rest { + if (_isClosed) { + throw _failClosed(); + } + var request = new _RestRequest(this); + _isClosed = true; + _addRequest(request); + return request.stream; + } + + /// Skips the next [count] *data* events. + /// + /// The [count] must be non-negative. + /// + /// When successful, this is equivalent to using [take] + /// and ignoring the result. + /// + /// If an error occurs before `count` data events have been skipped, + /// the returned future completes with that error instead. + /// + /// If the stream closes before `count` data events, + /// the remaining unskipped event count is returned. + /// If the returned future completes with the integer `0`, + /// then all events were succssfully skipped. If the value + /// is greater than zero then the stream ended early. + Future skip(int count) { + if (count < 0) throw new RangeError.range(count, 0, null, "count"); + if (!_isClosed) { + var request = new _SkipRequest(count); + _addRequest(request); + return request.future; + } + throw _failClosed(); + } + + /// Requests the next [count] data events as a list. + /// + /// The [count] must be non-negative. + /// + /// Equivalent to calling [next] `count` times and + /// storing the data values in a list. + /// + /// If an error occurs before `count` data events has + /// been collected, the returned future completes with + /// that error instead. + /// + /// If the stream closes before `count` data events, + /// the returned future completes with the list + /// of data collected so far. That is, the returned + /// list may have fewer than [count] elements. + Future> take(int count) { + if (count < 0) throw new RangeError.range(count, 0, null, "count"); + if (!_isClosed) { + var request = new _TakeRequest(count); + _addRequest(request); + return request.future; + } + throw _failClosed(); + } + + /// Cancels the underlying stream subscription. + /// + /// The cancel operation waits until all previously requested + /// events have been processed, then it cancels the subscription + /// providing the events. + /// + /// The returned future completes with the result of calling + /// `cancel`. + /// + /// After calling `cancel`, no further events can be requested. + /// None of [next], [rest], [skip], [take] or [cancel] may be + /// called again. + Future cancel() { + if (!_isClosed) { + _isClosed = true; + var request = new _CancelRequest(this); + _addRequest(request); + return request.future; + } + throw _failClosed(); + } + + /// Returns an error for when a request is made after cancel. + /// + /// Returns a [StateError] with a message saying that either + /// [cancel] or [rest] have already been called. + Error _failClosed() { + return new StateError("Already cancelled"); + } + + // Callbacks receiving the events of the source stream. + + void _onData(T data) { + _eventQueue.add(new Result.value(data)); + _checkQueues(); + } + + void _onError(error, StackTrace stack) { + _eventQueue.add(new Result.error(error, stack)); + _checkQueues(); + } + + void _onDone() { + _subscription = null; + _isDone = true; + _closeAllRequests(); + } + + // Request queue management. + + /// Adds a new request to the queue. + void _addRequest(_EventRequest request) { + if (_isDone) { + assert(_requestQueue.isEmpty); + if (!request.addEvents(_eventQueue)) { + request.close(_eventQueue); + } + return; + } + if (_requestQueue.isEmpty) { + if (request.addEvents(_eventQueue)) return; + _ensureListening(); + } + _requestQueue.add(request); + + } + + /// Ensures that we are listening on events from [_sourceStream]. + /// + /// Resumes subscription on [_sourceStream], or creates it if necessary. + StreamSubscription _ensureListening() { + assert(!_isDone); + if (_subscription == null) { + _subscription = + _sourceStream.listen(_onData, onError: _onError, onDone: _onDone); + } else { + _subscription.resume(); + } + } + + /// Removes all requests and closes them. + /// + /// Used when the source stream is done. + /// After this, no further requests will be added to the queue, + /// requests are immediately served entirely by events already in the event + /// queue, if any. + void _closeAllRequests() { + assert(_isDone); + while (_requestQueue.isNotEmpty) { + var request = _requestQueue.removeFirst(); + if (!request.addEvents(_eventQueue)) { + request.close(_eventQueue); + } + } + } + + /// Matches events with requests. + /// + /// Called after receiving an event. + void _checkQueues() { + while (_requestQueue.isNotEmpty) { + if (_requestQueue.first.addEvents(_eventQueue)) { + _requestQueue.removeFirst(); + } else { + return; + } + } + if (!_isDone) { + _subscription.pause(); + } + } + + /// Extracts the subscription and makes this stream queue unusable. + /// + /// Can only be used by the very last request. + StreamSubscription _dispose() { + assert(_isClosed); + var subscription = _subscription; + _subscription = null; + _isDone = true; + return subscription; + } +} + +/// Request object that receives events when they arrive, until fulfilled. +/// +/// Each request that cannot be fulfilled immediately is represented by +/// an `_EventRequest` object in the request queue. +/// +/// Events from the source stream are sent to the first request in the +/// queue until it reports itself as [isComplete]. +/// +/// When the first request in the queue `isComplete`, either when becoming +/// the first request or after receiving an event, its [close] methods is +/// called. +/// +/// The [close] method is also called immediately when the source stream +/// is done. +abstract class _EventRequest implements EventSink { + /// Handle available events. + /// + /// The available events are provided as a queue. The `addEvents` function + /// should only remove events from the front of the event queue, e.g., + /// using [removeFirst]. + /// + /// Returns `true` if the request is completed, or `false` if it needs + /// more events. + /// The call may keep events in the queue until the requeust is complete, + /// or it may remove them immediately. + /// + /// If the method returns true, the request is considered fulfilled, and + /// will never be called again. + /// + /// This method is called when a request reaches the front of the request + /// queue, and if it returns `false`, it's called again every time a new event + /// becomes available, or when the stream closes. + bool addEvents(Queue events); + + /// Complete the request. + /// + /// This is called when the source stream is done before the request + /// had a chance to receive all its events. That is, after a call + /// to [addEvents] has returned `false`. + /// If there are any unused events available, they are in the [events] queue. + /// No further events will become available. + /// + /// The queue should only remove events from the front of the event queue, + /// e.g., using [removeFirst]. + /// + /// If the request kept events in the queue after an [addEvents] call, + /// this is the last chance to use them. + void close(Queue events); +} + +/// Request for a [StreamQueue.next] call. +/// +/// Completes the returned future when receiving the first event, +/// and is then complete. +class _NextRequest implements _EventRequest { + /// Completer for the future returned by [StreamQueue.next]. + final Completer _completer; + + _NextRequest() : _completer = new Completer(); + + Future get future => _completer.future; + + bool addEvents(Queue events) { + if (events.isEmpty) return false; + events.removeFirst().complete(_completer); + return true; + } + + void close(Queue events) { + var errorFuture = + new Future.sync(() => throw new StateError("No elements")); + _completer.complete(errorFuture); + } +} + +/// Request for a [StreamQueue.skip] call. +class _SkipRequest implements _EventRequest { + /// Completer for the future returned by the skip call. + final Completer _completer = new Completer(); + + /// Number of remaining events to skip. + /// + /// The request [isComplete] when the values reaches zero. + /// + /// Decremented when an event is seen. + /// Set to zero when an error is seen since errors abort the skip request. + int _eventsToSkip; + + _SkipRequest(this._eventsToSkip); + + /// The future completed when the correct number of events have been skipped. + Future get future => _completer.future; + + bool addEvents(Queue events) { + while (_eventsToSkip > 0) { + if (events.isEmpty) return false; + _eventsToSkip--; + var event = events.removeFirst(); + if (event.isError) { + event.complete(_completer); + return true; + } + } + _completer.complete(0); + return true; + } + + void close(Queue events) { + _completer.complete(_eventsToSkip); + } +} + +/// Request for a [StreamQueue.take] call. +class _TakeRequest implements _EventRequest { + /// Completer for the future returned by the take call. + final Completer _completer; + + /// List collecting events until enough have been seen. + final List _list = []; + + /// Number of events to capture. + /// + /// The request [isComplete] when the length of [_list] reaches + /// this value. + final int _eventsToTake; + + _TakeRequest(this._eventsToTake) : _completer = new Completer>(); + + /// The future completed when the correct number of events have been captured. + Future get future => _completer.future; + + bool addEvents(Queue events) { + while (_list.length < _eventsToTake) { + if (events.isEmpty) return false; + var result = events.removeFirst(); + if (result.isError) { + result.complete(_completer); + return true; + } + _list.add(result.asValue.value); + } + _completer.complete(_list); + return true; + } + + void close(Queue events) { + _completer.complete(_list); + } +} + +/// Request for a [StreamQueue.cancel] call. +/// +/// The request needs no events, it just waits in the request queue +/// until all previous events are fulfilled, then it cancels the stream queue +/// source subscription. +class _CancelRequest implements _EventRequest { + /// Completer for the future returned by the `cancel` call. + final Completer _completer = new Completer(); + + /// The [StreamQueue] object that has this request queued. + /// + /// When the event is completed, it needs to cancel the active subscription + /// of the `StreamQueue` object, if any. + final StreamQueue _streamQueue; + + _CancelRequest(this._streamQueue); + + /// The future completed when the cancel request is completed. + Future get future => _completer.future; + + bool addEvents(Queue events) { + _shutdown(); + return true; + } + + void close(_) { + _shutdown(); + } + + void _shutdown() { + if (_streamQueue._subscription == null) { + _completer.complete(); + } else { + _completer.complete(_streamQueue._dispose().cancel()); + } + } +} + +/// Request for a [StreamQueue.rest] call. +/// +/// The request is always complete, it just waits in the request queue +/// until all previous events are fulfilled, then it takes over the +/// stream events subscription and creates a stream from it. +class _RestRequest implements _EventRequest { + /// Completer for the stream returned by the `rest` call. + final StreamCompleter _completer = new StreamCompleter(); + + /// The [StreamQueue] object that has this request queued. + /// + /// When the event is completed, it needs to cancel the active subscription + /// of the `StreamQueue` object, if any. + final StreamQueue _streamQueue; + + _RestRequest(this._streamQueue); + + /// The stream which will contain the remaining events of [_streamQueue]. + Stream get stream => _completer.stream; + + bool addEvents(Queue events) { + _completeStream(events); + return true; + } + + void close(Queue events) { + _completeStream(events); + } + + void _completeStream(Queue events) { + if (events.isEmpty) { + if (_streamQueue._isDone) { + _completer.setEmpty(); + } else { + _completer.setSourceStream(_getRestStream()); + } + } else { + // There are prefetched events which needs to be added before the + // remaining stream. + var controller = new StreamController(); + for (var event in events) { + event.addTo(controller); + } + controller.addStream(_getRestStream(), cancelOnError: false) + .whenComplete(controller.close); + _completer.setSourceStream(controller.stream); + } + } + + /// Create a stream from the rest of [_streamQueue]'s subscription. + Stream _getRestStream() { + if (_streamQueue._isDone) { + var controller = new StreamController()..close(); + return controller.stream; + // TODO(lrn). Use the following when 1.11 is released. + // return new Stream.empty(); + } + if (_streamQueue._subscription == null) { + return _streamQueue._sourceStream; + } + var subscription = _streamQueue._dispose(); + subscription.resume(); + return new SubscriptionStream(subscription); + } +} + +/// Request for a [StreamQueue.hasNext] call. +/// +/// Completes the [future] with `true` if it sees any event, +/// but doesn't consume the event. +/// If the request is closed without seeing an event, then +/// the [future] is completed with `false`. +class _HasNextRequest implements _EventRequest { + final Completer _completer = new Completer(); + + Future get future => _completer.future; + + bool addEvents(Queue events) { + if (events.isNotEmpty) { + _completer.complete(true); + return true; + } + return false; + } + + void close(_) { + _completer.complete(false); + } +} diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart new file mode 100644 index 00000000..a1d533f8 --- /dev/null +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -0,0 +1,101 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.subscription_stream; + +import 'dart:async'; + +import "delegating_stream_subscription.dart"; + +/// A [Stream] adapter for a [StreamSubscription]. +/// +/// This class allows as `StreamSubscription` to be treated as a `Stream`. +/// +/// The subscription is paused until the stream is listened to, +/// then it is resumed and the events are passed on to the +/// stream's new subscription. +/// +/// This class assumes that is has control over the original subscription. +/// If other code is accessing the subscription, results may be unpredictable. +class SubscriptionStream extends Stream { + /// The subscription providing the events for this stream. + StreamSubscription _source; + + /// Create a single-subscription `Stream` from [subscription]. + /// + /// The `subscription` should not be paused. This class will not resume prior + /// pauses, so being paused is indistinguishable from not providing any + /// events. + /// + /// If the `subscription` doesn't send any `done` events, neither will this + /// stream. That may be an issue if `subscription` was made to cancel on + /// an error. + SubscriptionStream(StreamSubscription subscription) + : _source = subscription { + _source.pause(); + // Clear callbacks to avoid keeping them alive unnecessarily. + _source.onData(null); + _source.onError(null); + _source.onDone(null); + } + + StreamSubscription listen(void onData(T event), + {Function onError, + void onDone(), + bool cancelOnError}) { + if (_source == null) { + throw new StateError("Stream has already been listened to."); + } + cancelOnError = (true == cancelOnError); + var subscription = _source; + _source = null; + var result; + if (cancelOnError) { + result = new _CancelOnErrorSubscriptionWrapper(subscription); + } else { + // Wrap the subscription to ensure correct type parameter. + result = new DelegatingStreamSubscription(subscription); + } + result.onData(onData); + result.onError(onError); + result.onDone(onDone); + subscription.resume(); + return result; + } +} + +/// Subscription wrapper that cancels on error. +/// +/// Used by [SubscriptionStream] when forwarding a subscription +/// created with `cancelOnError` as `true` to one with (assumed) +/// `cancelOnError` as `false`. It automatically cancels the +/// source subscription on the first error. +class _CancelOnErrorSubscriptionWrapper + extends DelegatingStreamSubscription { + _CancelOnErrorSubscriptionWrapper(StreamSubscription subscription) + : super(subscription); + + void onError(Function handleError) { + // Cancel when receiving an error. + super.onError((error, StackTrace stackTrace) { + var cancelFuture = super.cancel(); + if (cancelFuture != null) { + // Wait for the cancel to complete before sending the error event. + cancelFuture.whenComplete(() { + if (handleError is ZoneBinaryCallback) { + handleError(error, stackTrace); + } else { + handleError(error); + } + }); + } else { + if (handleError is ZoneBinaryCallback) { + handleError(error, stackTrace); + } else { + handleError(error); + } + } + }); + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e19f5003..74f5ff95 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.2.1-dev +version: 1.3.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart new file mode 100644 index 00000000..eb233305 --- /dev/null +++ b/pkgs/async/test/stream_completer_test.dart @@ -0,0 +1,371 @@ +// Copyright (c) 2015, 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. + +import "dart:async"; + +import "package:async/async.dart" show StreamCompleter; +import "package:test/test.dart"; + +import "utils.dart"; + +main() { + test("a stream is linked before listening", () async { + var completer = new StreamCompleter(); + completer.setSourceStream(createStream()); + expect(completer.stream.toList(), completion([1, 2, 3, 4])); + }); + + test("listened to before a stream is linked", () async { + var completer = new StreamCompleter(); + var done = completer.stream.toList(); + await flushMicrotasks(); + completer.setSourceStream(createStream()); + expect(done, completion([1, 2, 3, 4])); + }); + + test("cancel before linking a stream doesn't listen on stream", () async { + var completer = new StreamCompleter(); + var subscription = completer.stream.listen(null); + subscription.pause(); // Should be ignored. + subscription.cancel(); + completer.setSourceStream(new UnusableStream()); // Doesn't throw. + }); + + test("listen and pause before linking stream", () async { + var controller = new StreamCompleter(); + var events = []; + var subscription = controller.stream.listen(events.add); + var done = subscription.asFuture(); + subscription.pause(); + var sourceController = new StreamController(); + sourceController..add(1)..add(2)..add(3)..add(4); + controller.setSourceStream(sourceController.stream); + await flushMicrotasks(); + expect(sourceController.hasListener, isTrue); + expect(sourceController.isPaused, isTrue); + expect(events, []); + subscription.resume(); + await flushMicrotasks(); + expect(sourceController.hasListener, isTrue); + expect(sourceController.isPaused, isFalse); + expect(events, [1, 2, 3, 4]); + sourceController.close(); + await done; + expect(events, [1, 2, 3, 4]); + }); + + test("pause more than once", () async { + var completer = new StreamCompleter(); + var events = []; + var subscription = completer.stream.listen(events.add); + var done = subscription.asFuture(); + subscription.pause(); + subscription.pause(); + subscription.pause(); + completer.setSourceStream(createStream()); + for (int i = 0; i < 3; i++) { + await flushMicrotasks(); + expect(events, []); + subscription.resume(); + } + await done; + expect(events, [1, 2, 3, 4]); + }); + + test("cancel new stream before source is done", () async { + var completer = new StreamCompleter(); + var listened = false; + var lastEvent = -1; + var controller = new StreamController(); + var subscription; + subscription = completer.stream.listen( + (value) { + expect(value, lessThan(3)); + lastEvent = value; + if (value == 2) { + subscription.cancel(); + } + }, + onError: unreachable("error"), + onDone: unreachable("done"), + cancelOnError: true); + completer.setSourceStream(controller.stream); + expect(controller.hasListener, isTrue); + + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + controller.add(1); + + await flushMicrotasks(); + expect(lastEvent, 1); + expect(controller.hasListener, isTrue); + controller.add(2); + + await flushMicrotasks(); + expect(lastEvent, 2); + expect(controller.hasListener, isFalse); + }); + + test("complete with setEmpty before listening", () async { + var completer = new StreamCompleter(); + completer.setEmpty(); + var done = new Completer(); + completer.stream.listen( + unreachable("data"), + onError: unreachable("error"), + onDone: done.complete); + await done.future; + }); + + test("complete with setEmpty after listening", () async { + var completer = new StreamCompleter(); + var done = new Completer(); + completer.stream.listen( + unreachable("data"), + onError: unreachable("error"), + onDone: done.complete); + completer.setEmpty(); + await done.future; + }); + + test("source stream isn't listened to until completer stream is", () async { + var completer = new StreamCompleter(); + var controller; + controller = new StreamController(onListen: () { + scheduleMicrotask(controller.close); + }); + + completer.setSourceStream(controller.stream); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + var subscription = completer.stream.listen(null); + expect(controller.hasListener, isTrue); + await subscription.asFuture(); + }); + + test("cancelOnError true when listening before linking stream", () async { + var completer = new StreamCompleter(); + var listened = false; + var canceled = false; + var lastEvent = -1; + var controller = new StreamController(); + var subscription = completer.stream.listen( + (value) { + expect(value, lessThan(3)); + lastEvent = value; + }, + onError: (value) { + expect(value, "3"); + lastEvent = value; + }, + onDone: unreachable("done"), + cancelOnError: true); + completer.setSourceStream(controller.stream); + expect(controller.hasListener, isTrue); + + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + controller.add(1); + + await flushMicrotasks(); + expect(lastEvent, 1); + expect(controller.hasListener, isTrue); + controller.add(2); + + await flushMicrotasks(); + expect(lastEvent, 2); + expect(controller.hasListener, isTrue); + controller.addError("3"); + + await flushMicrotasks(); + expect(lastEvent, "3"); + expect(controller.hasListener, isFalse); + }); + + test("cancelOnError true when listening after linking stream", () async { + var completer = new StreamCompleter(); + var lastEvent = -1; + var controller = new StreamController(); + completer.setSourceStream(controller.stream); + controller.add(1); + expect(controller.hasListener, isFalse); + + var subscription = completer.stream.listen( + (value) { + expect(value, lessThan(3)); + lastEvent = value; + }, + onError: (value) { + expect(value, "3"); + lastEvent = value; + }, + onDone: unreachable("done"), + cancelOnError: true); + + expect(controller.hasListener, isTrue); + + await flushMicrotasks(); + expect(lastEvent, 1); + expect(controller.hasListener, isTrue); + controller.add(2); + + await flushMicrotasks(); + expect(lastEvent, 2); + expect(controller.hasListener, isTrue); + controller.addError("3"); + + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + }); + + test("linking a stream after setSourceStream before listen", () async { + var completer = new StreamCompleter(); + completer.setSourceStream(createStream()); + expect(() => completer.setSourceStream(createStream()), throws); + expect(() => completer.setEmpty(createStream()), throws); + await completer.stream.toList(); + // Still fails after source is done + expect(() => completer.setSourceStream(createStream()), throws); + expect(() => completer.setEmpty(createStream()), throws); + }); + + test("linking a stream after setSourceStream after listen", () async { + var completer = new StreamCompleter(); + var list = completer.stream.toList(); + completer.setSourceStream(createStream()); + expect(() => completer.setSoureStream(createStream()), throws); + expect(() => completer.stEmpty(createStream()), throws); + await list; + // Still fails after source is done. + expect(() => completer.setSoureStream(createStream()), throws); + expect(() => completer.stEmpty(createStream()), throws); + }); + + test("linking a stream after setEmpty before listen", () async { + var completer = new StreamCompleter(); + completer.setEmpty(); + expect(() => completer.setSoureStream(createStream()), throws); + expect(() => completer.stEmpty(createStream()), throws); + await completer.stream.toList(); + // Still fails after source is done + expect(() => completer.setSoureStream(createStream()), throws); + expect(() => completer.stEmpty(createStream()), throws); + }); + + test("linking a stream after setEmpty() after listen", () async { + var completer = new StreamCompleter(); + var list = completer.stream.toList(); + completer.setEmpty(); + expect(() => completer.setSoureStream(createStream()), throws); + expect(() => completer.stEmpty(createStream()), throws); + await list; + // Still fails after source is done. + expect(() => completer.setSoureStream(createStream()), throws); + expect(() => completer.stEmpty(createStream()), throws); + }); + + test("listening more than once after setting stream", () async { + var completer = new StreamCompleter(); + completer.setSourceStream(createStream()); + var list = completer.stream.toList(); + expect(() => completer.stream.oList(), throws); + await list; + expect(() => completer.stream.oList(), throws); + }); + + test("listening more than once before setting stream", () async { + var completer = new StreamCompleter(); + var list = completer.stream.toList(); + expect(() => completer.stream.oList(), throws); + }); + + test("setting onData etc. before and after setting stream", () async { + var completer = new StreamCompleter(); + var controller = new StreamController(); + var subscription = completer.stream.listen(null); + var lastEvent = 0; + subscription.onData((value) => lastEvent = value); + subscription.onError((value) => lastEvent = "$value"); + subscription.onDone(() => lastEvent = -1); + completer.setSourceStream(controller.stream); + await flushMicrotasks(); + controller.add(1); + await flushMicrotasks(); + expect(lastEvent, 1); + controller.addError(2); + await flushMicrotasks(); + expect(lastEvent, "2"); + subscription.onData((value) => lastEvent = -value); + subscription.onError((value) => lastEvent = "${-value}"); + controller.add(1); + await flushMicrotasks(); + expect(lastEvent, -1); + controller.addError(2); + await flushMicrotasks(); + expect(lastEvent, "-2"); + controller.close(); + await flushMicrotasks(); + expect(lastEvent, -1); + }); + + test("pause w/ resume future accross setting stream", () async { + var completer = new StreamCompleter(); + var resume = new Completer(); + var subscription = completer.stream.listen(unreachable("data")); + var lastEvent = 0; + subscription.pause(resume.future); + await flushMicrotasks(); + completer.setSourceStream(createStream()); + await flushMicrotasks(); + resume.complete(); + var events = []; + subscription.onData(events.add); + await subscription.asFuture(); + expect(events, [1, 2, 3, 4]); + }); + + test("asFuture with error accross setting stream", () async { + var completer = new StreamCompleter(); + var controller = new StreamController(); + var subscription = completer.stream.listen(unreachable("data"), + cancelOnError: false); + var done = subscription.asFuture(); + expect(controller.hasListener, isFalse); + completer.setSourceStream(controller.stream); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + controller.addError(42); + await done.then(unreachable("data"), onError: (error) { + expect(error, 42); + }); + expect(controller.hasListener, isFalse); + }); +} + +Stream createStream() async* { + yield 1; + await flushMicrotasks(); + yield 2; + await flushMicrotasks(); + yield 3; + await flushMicrotasks(); + yield 4; +} + +/// A zero-millisecond timer should wait until after all microtasks. +Future flushMicrotasks() => new Future.delayed(Duration.ZERO); + +/// A generic unreachable callback function. +/// +/// Returns a function that fails the test if it is ever called. +unreachable(String name) => ([a, b]) => fail("Unreachable: $name"); + +/// A badly behaved stream which throws if it's ever listened to. +/// +/// Can be used to test cases where a stream should not be used. +class UnusableStream extends Stream { + listen(onData, {onError, onDone, cancelOnError}) { + throw new UnimplementedError("Gotcha!"); + } +} diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart new file mode 100644 index 00000000..dcbf9211 --- /dev/null +++ b/pkgs/async/test/stream_queue_test.dart @@ -0,0 +1,632 @@ +// Copyright (c) 2015, 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 filevents. + +import "dart:async"; + +import "package:async/async.dart" show StreamQueue; +import "package:test/test.dart"; + +import "utils.dart"; + +main() { + group("source stream", () { + test("is listened to on first request, paused between requests", () async { + var controller = new StreamController(); + var events = new StreamQueue(controller.stream); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + var next = events.next; + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isFalse); + + controller.add(1); + + expect(await next, 1); + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isTrue); + + next = events.next; + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isFalse); + + controller.add(2); + + expect(await next, 2); + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isTrue); + + var cancel = events.cancel(); + expect(controller.hasListener, isFalse); + }); + }); + + group("next operation", () { + test("simple sequence of requests", () async { + var events = new StreamQueue(createStream()); + for (int i = 1; i <= 4; i++) { + expect(await events.next, i); + } + expect(events.next, throwsStateError); + }); + + test("multiple requests at the same time", () async { + var events = new StreamQueue(createStream()); + var result = await Future.wait( + [events.next, events.next, events.next, events.next]); + expect(result, [1, 2, 3, 4]); + await events.cancel(); + }); + + test("sequence of requests with error", () async { + var events = new StreamQueue(createErrorStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(events.next, throwsA("To err is divine!")); + expect(await events.next, 4); + await events.cancel(); + }); + }); + + group("skip operation", () { + test("of two elements in the middle of sequence", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.skip(2), 0); + expect(await events.next, 4); + await events.cancel(); + }); + + test("with negative/bad arguments throws", () async { + var events = new StreamQueue(createStream()); + expect(() => events.skip(-1), throwsArgumentError); + // A non-int throws either a type error or an argument error, + // depending on whether it's checked mode or not. + expect(await events.next, 1); // Did not consume event. + expect(() => events.skip(-1), throwsArgumentError); + expect(await events.next, 2); // Did not consume event. + await events.cancel(); + }); + + test("of 0 elements works", () async { + var events = new StreamQueue(createStream()); + expect(events.skip(0), completion(0)); + expect(events.next, completion(1)); + expect(events.skip(0), completion(0)); + expect(events.next, completion(2)); + expect(events.skip(0), completion(0)); + expect(events.next, completion(3)); + expect(events.skip(0), completion(0)); + expect(events.next, completion(4)); + expect(events.skip(0), completion(0)); + expect(events.skip(5), completion(5)); + expect(events.next, throwsStateError); + await events.cancel(); + }); + + test("of too many events ends at stream start", () async { + var events = new StreamQueue(createStream()); + expect(await events.skip(6), 2); + await events.cancel(); + }); + + test("of too many events after some events", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.skip(6), 4); + await events.cancel(); + }); + + test("of too many events ends at stream end", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.next, 3); + expect(await events.next, 4); + expect(await events.skip(2), 2); + await events.cancel(); + }); + + test("of events with error", () async { + var events = new StreamQueue(createErrorStream()); + expect(events.skip(4), throwsA("To err is divine!")); + expect(await events.next, 4); + await events.cancel(); + }); + + test("of events with error, and skip again after", () async { + var events = new StreamQueue(createErrorStream()); + expect(events.skip(4), throwsA("To err is divine!")); + expect(events.skip(2), completion(1)); + await events.cancel(); + }); + + test("multiple skips at same time complete in order.", () async { + var events = new StreamQueue(createStream()); + var skip1 = events.skip(1); + var skip2 = events.skip(0); + var skip3 = events.skip(4); + var skip4 = events.skip(1); + var index = 0; + // Check that futures complete in order. + sequence(expectedValue, sequenceIndex) => (value) { + expect(value, expectedValue); + expect(index, sequenceIndex); + index++; + } + await Future.wait([skip1.then(sequence(0, 0)), + skip2.then(sequence(0, 1)), + skip3.then(sequence(1, 2)), + skip4.then(sequence(1, 3))]); + await events.cancel(); + }); + }); + + group("take operation", () { + test("as simple take of events", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.take(2), [2, 3]); + expect(await events.next, 4); + await events.cancel(); + }); + + test("of 0 events", () async { + var events = new StreamQueue(createStream()); + expect(events.take(0), completion([])); + expect(events.next, completion(1)); + expect(events.take(0), completion([])); + expect(events.next, completion(2)); + expect(events.take(0), completion([])); + expect(events.next, completion(3)); + expect(events.take(0), completion([])); + expect(events.next, completion(4)); + expect(events.take(0), completion([])); + expect(events.take(5), completion([])); + expect(events.next, throwsStateError); + await events.cancel(); + }); + + test("with bad arguments throws", () async { + var events = new StreamQueue(createStream()); + expect(() => events.take(-1), throwsArgumentError); + expect(await events.next, 1); // Did not consume event. + expect(() => events.take(-1), throwsArgumentError); + expect(await events.next, 2); // Did not consume event. + await events.cancel(); + }); + + test("of too many arguments", () async { + var events = new StreamQueue(createStream()); + expect(await events.take(6), [1, 2, 3, 4]); + await events.cancel(); + }); + + test("too large later", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.take(6), [3, 4]); + await events.cancel(); + }); + + test("error", () async { + var events = new StreamQueue(createErrorStream()); + expect(events.take(4), throwsA("To err is divine!")); + expect(await events.next, 4); + await events.cancel(); + }); + }); + + group("rest operation", () { + test("after single next", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.rest.toList(), [2, 3, 4]); + }); + + test("at start", () async { + var events = new StreamQueue(createStream()); + expect(await events.rest.toList(), [1, 2, 3, 4]); + }); + + test("at end", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.next, 3); + expect(await events.next, 4); + expect(await events.rest.toList(), isEmpty); + }); + + test("after end", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.next, 3); + expect(await events.next, 4); + expect(events.next, throwsStateError); + expect(await events.rest.toList(), isEmpty); + }); + + test("after receiving done requested before", () async { + var events = new StreamQueue(createStream()); + var next1 = events.next; + var next2 = events.next; + var next3 = events.next; + var rest = events.rest; + for (int i = 0; i < 10; i++) { + await flushMicrotasks(); + } + expect(await next1, 1); + expect(await next2, 2); + expect(await next3, 3); + expect(await rest.toList(), [4]); + }); + + test("with an error event error", () async { + var events = new StreamQueue(createErrorStream()); + expect(await events.next, 1); + var rest = events.rest; + var events2 = new StreamQueue(rest); + expect(await events2.next, 2); + expect(events2.next, throwsA("To err is divine!")); + expect(await events2.next, 4); + }); + + test("closes the events, prevents other operations", () async { + var events = new StreamQueue(createStream()); + var stream = events.rest; + expect(() => events.next, throwsStateError); + expect(() => events.skip(1), throwsStateError); + expect(() => events.take(1), throwsStateError); + expect(() => events.rest, throwsStateError); + expect(() => events.cancel(), throwsStateError); + }); + + test("forwards to underlying stream", () async { + var cancel = new Completer(); + var controller = new StreamController(onCancel: () => cancel.future); + var events = new StreamQueue(controller.stream); + expect(controller.hasListener, isFalse); + var next = events.next; + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isFalse); + + controller.add(1); + expect(await next, 1); + expect(controller.isPaused, isTrue); + + var rest = events.rest; + var subscription = rest.listen(null); + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isFalse); + + var lastEvent; + subscription.onData((value) => lastEvent = value); + + controller.add(2); + + await flushMicrotasks(); + expect(lastEvent, 2); + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isFalse); + + subscription.pause(); + expect(controller.isPaused, isTrue); + + controller.add(3); + + await flushMicrotasks(); + expect(lastEvent, 2); + subscription.resume(); + + await flushMicrotasks(); + expect(lastEvent, 3); + + var cancelFuture = subscription.cancel(); + expect(controller.hasListener, isFalse); + cancel.complete(42); + expect(cancelFuture, completion(42)); + }); + }); + + group("close operation", () { + test("closes the events, prevents any other operation", () async { + var events = new StreamQueue(createStream()); + await events.cancel(); + expect(() => events.next, throwsStateError); + expect(() => events.skip(1), throwsStateError); + expect(() => events.take(1), throwsStateError); + expect(() => events.rest, throwsStateError); + expect(() => events.cancel(), throwsStateError); + }); + + test("cancels underlying subscription, returns result", () async { + var cancelFuture = new Future.value(42); + var controller = new StreamController(onCancel: () => cancelFuture); + var events = new StreamQueue(controller.stream); + controller.add(1); + expect(await events.next, 1); + expect(await events.cancel(), 42); + }); + }); + + + group("hasNext operation", () { + test("true at start", () async { + var events = new StreamQueue(createStream()); + expect(await events.hasNext, isTrue); + }); + + test("true after start", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, isTrue); + }); + + test("true at end", () async { + var events = new StreamQueue(createStream()); + for (int i = 1; i <= 4; i++) { + expect(await events.next, i); + } + expect(await events.hasNext, isFalse); + }); + + test("true when enqueued", () async { + var events = new StreamQueue(createStream()); + var values = []; + for (int i = 1; i <= 3; i++) { + events.next.then(values.add); + } + expect(values, isEmpty); + expect(await events.hasNext, isTrue); + expect(values, [1, 2, 3]); + }); + + test("false when enqueued", () async { + var events = new StreamQueue(createStream()); + var values = []; + for (int i = 1; i <= 4; i++) { + events.next.then(values.add); + } + expect(values, isEmpty); + expect(await events.hasNext, isFalse); + expect(values, [1, 2, 3, 4]); + }); + + test("true when data event", () async { + var controller = new StreamController(); + var events = new StreamQueue(controller.stream); + + var hasNext; + events.hasNext.then((result) { hasNext = result; }); + await flushMicrotasks(); + expect(hasNext, isNull); + controller.add(42); + expect(hasNext, isNull); + await flushMicrotasks(); + expect(hasNext, isTrue); + }); + + test("true when error event", () async { + var controller = new StreamController(); + var events = new StreamQueue(controller.stream); + + var hasNext; + events.hasNext.then((result) { hasNext = result; }); + await flushMicrotasks(); + expect(hasNext, isNull); + controller.addError("BAD"); + expect(hasNext, isNull); + await flushMicrotasks(); + expect(hasNext, isTrue); + expect(events.next, throwsA("BAD")); + }); + + test("- hasNext after hasNext", () async { + var events = new StreamQueue(createStream()); + expect(await events.hasNext, true); + expect(await events.hasNext, true); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.hasNext, true); + expect(await events.next, 2); + expect(await events.hasNext, true); + expect(await events.hasNext, true); + expect(await events.next, 3); + expect(await events.hasNext, true); + expect(await events.hasNext, true); + expect(await events.next, 4); + expect(await events.hasNext, false); + expect(await events.hasNext, false); + }); + + test("- next after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.next, 2); + expect(await events.next, 3); + }); + + test("- next after true, enqueued", () async { + var events = new StreamQueue(createStream()); + var responses = []; + var first = events.next.then(responses.add); + var hasSecond = events.hasNext.then(responses.add); + var second = events.next.then(responses.add); + do { + await flushMicrotasks(); + } while (responses.length < 3); + expect(responses, [1, true, 2]); + }); + + test("- skip 0 after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.skip(0), 0); + expect(await events.next, 2); + }); + + test("- skip 1 after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.skip(1), 0); + expect(await events.next, 3); + }); + + test("- skip 2 after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.skip(2), 0); + expect(await events.next, 4); + }); + + test("- take 0 after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.take(0), isEmpty); + expect(await events.next, 2); + }); + + test("- take 1 after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.take(1), [2]); + expect(await events.next, 3); + }); + + test("- take 2 after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + expect(await events.take(2), [2, 3]); + expect(await events.next, 4); + }); + + test("- rest after true", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.hasNext, true); + var stream = events.rest; + expect(await stream.toList(), [2, 3, 4]); + }); + + test("- rest after true, at last", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.next, 3); + expect(await events.hasNext, true); + var stream = events.rest; + expect(await stream.toList(), [4]); + }); + + test("- rest after false", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.next, 3); + expect(await events.next, 4); + expect(await events.hasNext, false); + var stream = events.rest; + expect(await stream.toList(), isEmpty); + }); + + test("- cancel after true on data", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.hasNext, true); + expect(await events.cancel(), null); + }); + + test("- cancel after true on error", () async { + var events = new StreamQueue(createErrorStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.hasNext, true); + expect(await events.cancel(), null); + }); + }); + + test("all combinations sequential skip/next/take operations", () async { + // Takes all combinations of two of next, skip and take, then ends with + // doing rest. Each of the first rounds do 10 events of each type, + // the rest does 20 elements. + var eventCount = 20 * (3 * 3 + 1); + var events = new StreamQueue(createLongStream(eventCount)); + + // Test expecting [startIndex .. startIndex + 9] as events using + // `next`. + nextTest(startIndex) { + for (int i = 0; i < 10; i++) { + expect(events.next, completion(startIndex + i)); + } + } + + // Test expecting 10 events to be skipped. + skipTest(startIndex) { + expect(events.skip(10), completion(0)); + } + + // Test expecting [startIndex .. startIndex + 9] as events using + // `take(10)`. + takeTest(startIndex) { + expect(events.take(10), + completion(new List.generate(10, (i) => startIndex + i))); + } + var tests = [nextTest, skipTest, takeTest]; + + int counter = 0; + // Run through all pairs of two tests and run them. + for (int i = 0; i < tests.length; i++) { + for (int j = 0; j < tests.length; j++) { + tests[i](counter); + tests[j](counter + 10); + counter += 20; + } + } + // Then expect 20 more events as a `rest` call. + expect(events.rest.toList(), + completion(new List.generate(20, (i) => counter + i))); + }); +} + +Stream createStream() async* { + yield 1; + await flushMicrotasks(); + yield 2; + await flushMicrotasks(); + yield 3; + await flushMicrotasks(); + yield 4; +} + +Stream createErrorStream() { + StreamController controller = new StreamController(); + () async { + controller.add(1); + await flushMicrotasks(); + controller.add(2); + await flushMicrotasks(); + controller.addError("To err is divine!"); + await flushMicrotasks(); + controller.add(4); + await flushMicrotasks(); + controller.close(); + }(); + return controller.stream; +} + +Stream createLongStream(int eventCount) async* { + for (int i = 0; i < eventCount; i++) yield i; +} diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart new file mode 100644 index 00000000..da77593e --- /dev/null +++ b/pkgs/async/test/subscription_stream_test.dart @@ -0,0 +1,176 @@ +// Copyright (c) 2015, 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. + +import "dart:async"; + +import "package:async/async.dart" show SubscriptionStream; +import "package:test/test.dart"; + +import "utils.dart"; + +main() { + test("subscription stream of an entire subscription", () async { + var stream = createStream(); + var subscription = stream.listen(null); + var subscriptionStream = new SubscriptionStream(subscription); + await flushMicrotasks(); + expect(subscriptionStream.toList(), completion([1, 2, 3, 4])); + }); + + test("subscription stream after two events", () async { + var stream = createStream(); + var skips = 0; + var completer = new Completer(); + var subscription; + subscription = stream.listen((value) { + ++skips; + expect(value, skips); + if (skips == 2) { + completer.complete(new SubscriptionStream(subscription)); + } + }); + var subscriptionStream = await completer.future; + await flushMicrotasks(); + expect(subscriptionStream.toList(), completion([3, 4])); + }); + + test("listening twice fails", () async { + var stream = createStream(); + var sourceSubscription = stream.listen(null); + var subscriptionStream = new SubscriptionStream(sourceSubscription); + var subscription = subscriptionStream.listen(null); + expect(() => subscriptionStream.listen(null), throws); + await subscription.cancel(); + }); + + test("pause and cancel passed through to original stream", () async { + var controller = new StreamController(onCancel: () async => 42); + var sourceSubscription = controller.stream.listen(null); + var subscriptionStream = new SubscriptionStream(sourceSubscription); + expect(controller.isPaused, isTrue); + var lastEvent; + var subscription = subscriptionStream.listen((value) { + lastEvent = value; + }); + controller.add(1); + + await flushMicrotasks(); + expect(lastEvent, 1); + expect(controller.isPaused, isFalse); + + subscription.pause(); + expect(controller.isPaused, isTrue); + + subscription.resume(); + expect(controller.isPaused, isFalse); + + expect(await subscription.cancel(), 42); + expect(controller.hasListener, isFalse); + }); + + group("cancelOnError source:", () { + for (var sourceCancels in [false, true]) { + group("${sourceCancels ? "yes" : "no"}:", () { + var subscriptionStream; + setUp(() { + var source = createErrorStream(); + var sourceSubscription = source.listen(null, + cancelOnError: sourceCancels); + subscriptionStream = new SubscriptionStream(sourceSubscription); + }); + + test("- subscriptionStream: no", () async { + var done = new Completer(); + var events = []; + var subscription = subscriptionStream.listen(events.add, + onError: events.add, + onDone: done.complete, + cancelOnError: false); + var expected = [1, 2, "To err is divine!"]; + if (sourceCancels) { + var timeout = done.future.timeout(const Duration(milliseconds: 5), + onTimeout: () => true); + expect(await timeout, true); + } else { + expected.add(4); + await done.future; + } + expect(events, expected); + }); + + test("- subscriptionStream: yes", () async { + var completer = new Completer(); + var events = []; + subscriptionStream.listen(events.add, + onError: (value) { + events.add(value); + completer.complete(); + }, + onDone: () => throw "should not happen", + cancelOnError: true); + await completer.future; + await flushMicrotasks(); + expect(events, [1, 2, "To err is divine!"]); + }); + }); + } + + for (var cancelOnError in [false, true]) { + group(cancelOnError ? "yes" : "no", () { + test("- no error, value goes to asFuture", () async { + var stream = createStream(); + var sourceSubscription = + stream.listen(null, cancelOnError: cancelOnError); + var subscriptionStream = + new SubscriptionStream(sourceSubscription); + var subscription = + subscriptionStream.listen(null, cancelOnError: cancelOnError); + expect(subscription.asFuture(42), completion(42)); + }); + + test("- error goes to asFuture", () async { + var stream = createErrorStream(); + var sourceSubscription = stream.listen(null, + cancelOnError: cancelOnError); + var subscriptionStream = + new SubscriptionStream(sourceSubscription); + + var subscription = + subscriptionStream.listen(null, cancelOnError: cancelOnError); + expect(subscription.asFuture(), throws); + }); + }); + } + }); +} + +Stream createStream() async* { + yield 1; + await flushMicrotasks(); + yield 2; + await flushMicrotasks(); + yield 3; + await flushMicrotasks(); + yield 4; +} + +Stream createErrorStream() { + StreamController controller = new StreamController(); + () async { + controller.add(1); + await flushMicrotasks(); + controller.add(2); + await flushMicrotasks(); + controller.addError("To err is divine!"); + await flushMicrotasks(); + controller.add(4); + await flushMicrotasks(); + controller.close(); + }(); + return controller.stream; +} + +Stream createLongStream() async* { + for (int i = 0; i < 200; i++) yield i; +} diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart new file mode 100644 index 00000000..d8b9ad53 --- /dev/null +++ b/pkgs/async/test/utils.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2015, 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. + +/// Helper utilities for testing. +library async.test.util; + +import "dart:async"; + +/// A zero-millisecond timer should wait until after all microtasks. +Future flushMicrotasks() => new Future.delayed(Duration.ZERO); + +/// A generic unreachable callback function. +/// +/// Returns a function that fails the test if it is ever called. +unreachable(String name) => ([a, b]) => fail("Unreachable: $name"); + +/// A badly behaved stream which throws if it's ever listened to. +/// +/// Can be used to test cases where a stream should not be used. +class UnusableStream extends Stream { + listen(onData, {onError, onDone, cancelOnError}) { + throw new UnimplementedError("Gotcha!"); + } +} From e30579ae4d1fed44035807f17f7cf343139322d2 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 1 Jul 2015 11:23:20 +0200 Subject: [PATCH 027/260] Fix syntax error which was accepted by the VM. Review URL: https://codereview.chromium.org//1219683012. --- pkgs/async/test/stream_queue_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index dcbf9211..a41faf9f 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -142,7 +142,6 @@ main() { expect(events.skip(2), completion(1)); await events.cancel(); }); - test("multiple skips at same time complete in order.", () async { var events = new StreamQueue(createStream()); var skip1 = events.skip(1); @@ -155,7 +154,7 @@ main() { expect(value, expectedValue); expect(index, sequenceIndex); index++; - } + }; await Future.wait([skip1.then(sequence(0, 0)), skip2.then(sequence(0, 1)), skip3.then(sequence(1, 2)), From cb844f3d34c0494b65e5ba85f58563e3cf0d7e9f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 1 Jul 2015 11:43:36 +0200 Subject: [PATCH 028/260] Fix type errors caught by analyzer. R=sgjesse@google.com Review URL: https://codereview.chromium.org//1215873004. --- pkgs/async/lib/src/stream_queue.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 5255b044..0018710b 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -286,7 +286,7 @@ class StreamQueue { /// Ensures that we are listening on events from [_sourceStream]. /// /// Resumes subscription on [_sourceStream], or creates it if necessary. - StreamSubscription _ensureListening() { + void _ensureListening() { assert(!_isDone); if (_subscription == null) { _subscription = @@ -354,7 +354,7 @@ class StreamQueue { /// /// The [close] method is also called immediately when the source stream /// is done. -abstract class _EventRequest implements EventSink { +abstract class _EventRequest { /// Handle available events. /// /// The available events are provided as a queue. The `addEvents` function @@ -471,7 +471,7 @@ class _TakeRequest implements _EventRequest { /// The future completed when the correct number of events have been captured. Future get future => _completer.future; - bool addEvents(Queue events) { + bool addEvents(Queue events) { while (_list.length < _eventsToTake) { if (events.isEmpty) return false; var result = events.removeFirst(); @@ -485,7 +485,7 @@ class _TakeRequest implements _EventRequest { return true; } - void close(Queue events) { + void close(Queue events) { _completer.complete(_list); } } From 46384464e9d855512f1013d5b666a5cc07ad2362 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 1 Jul 2015 12:12:12 +0200 Subject: [PATCH 029/260] Remove warnings in tests, and even fix analyzer hints. Review URL: https://codereview.chromium.org//1215293002. --- pkgs/async/test/stream_completer_test.dart | 65 +++++++------------ pkgs/async/test/stream_queue_test.dart | 9 +-- pkgs/async/test/subscription_stream_test.dart | 8 +-- pkgs/async/test/utils.dart | 1 + 4 files changed, 32 insertions(+), 51 deletions(-) diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index eb233305..cd3ceb9f 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -75,7 +75,6 @@ main() { test("cancel new stream before source is done", () async { var completer = new StreamCompleter(); - var listened = false; var lastEvent = -1; var controller = new StreamController(); var subscription; @@ -146,11 +145,9 @@ main() { test("cancelOnError true when listening before linking stream", () async { var completer = new StreamCompleter(); - var listened = false; - var canceled = false; var lastEvent = -1; var controller = new StreamController(); - var subscription = completer.stream.listen( + completer.stream.listen( (value) { expect(value, lessThan(3)); lastEvent = value; @@ -191,7 +188,7 @@ main() { controller.add(1); expect(controller.hasListener, isFalse); - var subscription = completer.stream.listen( + completer.stream.listen( (value) { expect(value, lessThan(3)); lastEvent = value; @@ -222,62 +219,62 @@ main() { test("linking a stream after setSourceStream before listen", () async { var completer = new StreamCompleter(); completer.setSourceStream(createStream()); - expect(() => completer.setSourceStream(createStream()), throws); - expect(() => completer.setEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); await completer.stream.toList(); // Still fails after source is done - expect(() => completer.setSourceStream(createStream()), throws); - expect(() => completer.setEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); }); test("linking a stream after setSourceStream after listen", () async { var completer = new StreamCompleter(); var list = completer.stream.toList(); completer.setSourceStream(createStream()); - expect(() => completer.setSoureStream(createStream()), throws); - expect(() => completer.stEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); await list; // Still fails after source is done. - expect(() => completer.setSoureStream(createStream()), throws); - expect(() => completer.stEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); }); test("linking a stream after setEmpty before listen", () async { var completer = new StreamCompleter(); completer.setEmpty(); - expect(() => completer.setSoureStream(createStream()), throws); - expect(() => completer.stEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); await completer.stream.toList(); // Still fails after source is done - expect(() => completer.setSoureStream(createStream()), throws); - expect(() => completer.stEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); }); test("linking a stream after setEmpty() after listen", () async { var completer = new StreamCompleter(); var list = completer.stream.toList(); completer.setEmpty(); - expect(() => completer.setSoureStream(createStream()), throws); - expect(() => completer.stEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); await list; // Still fails after source is done. - expect(() => completer.setSoureStream(createStream()), throws); - expect(() => completer.stEmpty(createStream()), throws); + expect(() => completer.setSourceStream(createStream()), throwsStateError); + expect(() => completer.setEmpty(), throwsStateError); }); test("listening more than once after setting stream", () async { var completer = new StreamCompleter(); completer.setSourceStream(createStream()); var list = completer.stream.toList(); - expect(() => completer.stream.oList(), throws); + expect(() => completer.stream.toList(), throwsStateError); await list; - expect(() => completer.stream.oList(), throws); + expect(() => completer.stream.toList(), throwsStateError); }); test("listening more than once before setting stream", () async { var completer = new StreamCompleter(); - var list = completer.stream.toList(); - expect(() => completer.stream.oList(), throws); + completer.stream.toList(); + expect(() => completer.stream.toList(), throwsStateError); }); test("setting onData etc. before and after setting stream", () async { @@ -313,7 +310,6 @@ main() { var completer = new StreamCompleter(); var resume = new Completer(); var subscription = completer.stream.listen(unreachable("data")); - var lastEvent = 0; subscription.pause(resume.future); await flushMicrotasks(); completer.setSourceStream(createStream()); @@ -352,20 +348,3 @@ Stream createStream() async* { await flushMicrotasks(); yield 4; } - -/// A zero-millisecond timer should wait until after all microtasks. -Future flushMicrotasks() => new Future.delayed(Duration.ZERO); - -/// A generic unreachable callback function. -/// -/// Returns a function that fails the test if it is ever called. -unreachable(String name) => ([a, b]) => fail("Unreachable: $name"); - -/// A badly behaved stream which throws if it's ever listened to. -/// -/// Can be used to test cases where a stream should not be used. -class UnusableStream extends Stream { - listen(onData, {onError, onDone, cancelOnError}) { - throw new UnimplementedError("Gotcha!"); - } -} diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index a41faf9f..e5fe25cf 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -37,7 +37,7 @@ main() { expect(controller.hasListener, isTrue); expect(controller.isPaused, isTrue); - var cancel = events.cancel(); + events.cancel(); expect(controller.hasListener, isFalse); }); }); @@ -283,6 +283,7 @@ main() { expect(() => events.take(1), throwsStateError); expect(() => events.rest, throwsStateError); expect(() => events.cancel(), throwsStateError); + expect(stream.toList(), completion([1, 2, 3, 4])); }); test("forwards to underlying stream", () async { @@ -454,9 +455,9 @@ main() { test("- next after true, enqueued", () async { var events = new StreamQueue(createStream()); var responses = []; - var first = events.next.then(responses.add); - var hasSecond = events.hasNext.then(responses.add); - var second = events.next.then(responses.add); + events.next.then(responses.add); + events.hasNext.then(responses.add); + events.next.then(responses.add); do { await flushMicrotasks(); } while (responses.length < 3); diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index da77593e..03f0ddde 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -83,10 +83,10 @@ main() { test("- subscriptionStream: no", () async { var done = new Completer(); var events = []; - var subscription = subscriptionStream.listen(events.add, - onError: events.add, - onDone: done.complete, - cancelOnError: false); + subscriptionStream.listen(events.add, + onError: events.add, + onDone: done.complete, + cancelOnError: false); var expected = [1, 2, "To err is divine!"]; if (sourceCancels) { var timeout = done.future.timeout(const Duration(milliseconds: 5), diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index d8b9ad53..b2ea885a 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -6,6 +6,7 @@ library async.test.util; import "dart:async"; +import "package:test/test.dart"; /// A zero-millisecond timer should wait until after all microtasks. Future flushMicrotasks() => new Future.delayed(Duration.ZERO); From f6fa53cb4cfae1432a019cef036e9a46c3c9f672 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 1 Jul 2015 12:20:54 -0700 Subject: [PATCH 030/260] ignore .packages R=nweiz@google.com Review URL: https://codereview.chromium.org//1220613004. --- pkgs/async/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/async/.gitignore b/pkgs/async/.gitignore index 89f7747c..7a2e8dd9 100644 --- a/pkgs/async/.gitignore +++ b/pkgs/async/.gitignore @@ -6,3 +6,4 @@ build/ packages pubspec.lock +.packages From b809c54b3fa956f197393b46dd7588743255a263 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 3 Jul 2015 12:11:55 +0200 Subject: [PATCH 031/260] Add handle method to ErrorResult. The handle method takes care of checking if the error handler function accepts two arguements or not, and calles it with error and stackTrace if possible, otherwise just with error. I noticed the pattern occuring multiple times when working with error results, so it would be better to have it handled in one place. Also changed result file to /// comments. R=sgjesse@google.com Review URL: https://codereview.chromium.org//1218813011. --- pkgs/async/lib/result.dart | 241 +++++++++++++------------------ pkgs/async/test/result_test.dart | 48 ++++++ 2 files changed, 151 insertions(+), 138 deletions(-) diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart index c734a37a..13e3dd94 100644 --- a/pkgs/async/lib/result.dart +++ b/pkgs/async/lib/result.dart @@ -2,31 +2,25 @@ // 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. -/** - * Capture asynchronous results into synchronous values, and release them again. - * - * Capturing a result (either a returned value or a thrown error) - * means converting it into a [Result] - - * either a [ValueResult] or an [ErrorResult]. - * - * This value can release itself by writing itself either to a - * [EventSink] or a [Completer], or by becoming a [Future]. - */ +/// Capture asynchronous results into synchronous values. +/// +/// Capturing a result (either a returned value or a thrown error) +/// means converting it into a [Result] - +/// either a [ValueResult] or an [ErrorResult]. +/// +/// This value can release itself by writing itself either to a +/// [EventSink] or a [Completer], or by becoming a [Future]. library dart.pkg.async.results; import "dart:async"; -/** - * The result of a computation. - */ +/// The result of a computation. abstract class Result { - /** - * Create a `Result` with the result of calling [computation]. - * - * This generates either a [ValueResult] with the value returned by - * calling `computation`, or an [ErrorResult] with an error thrown by - * the call. - */ + /// Create a `Result` with the result of calling [computation]. + /// + /// This generates either a [ValueResult] with the value returned by + /// calling `computation`, or an [ErrorResult] with an error thrown by + /// the call. factory Result(T computation()) { try { return new ValueResult(computation()); @@ -35,18 +29,14 @@ abstract class Result { } } - /** - * Create a `Result` holding a value. - * - * Alias for [ValueResult.ValueResult]. - */ + /// Create a `Result` holding a value. + /// + /// Alias for [ValueResult.ValueResult]. factory Result.value(T value) = ValueResult; - /** - * Create a `Result` holding an error. - * - * Alias for [ErrorResult.ErrorResult]. - */ + /// Create a `Result` holding an error. + /// + /// Alias for [ErrorResult.ErrorResult]. factory Result.error(Object error, [StackTrace stackTrace]) => new ErrorResult(error, stackTrace); @@ -58,118 +48,92 @@ abstract class Result { return v.asFuture; } - /** - * Capture the result of a future into a `Result` future. - * - * The resulting future will never have an error. - * Errors have been converted to an [ErrorResult] value. - */ + /// Capture the result of a future into a `Result` future. + /// + /// The resulting future will never have an error. + /// Errors have been converted to an [ErrorResult] value. static Future capture(Future future) { return future.then(_captureValue, onError: _captureError); } - /** - * Release the result of a captured future. - * - * Converts the [Result] value of the given [future] to a value or error - * completion of the returned future. - * - * If [future] completes with an error, the returned future completes with - * the same error. - */ + /// Release the result of a captured future. + /// + /// Converts the [Result] value of the given [future] to a value or error + /// completion of the returned future. + /// + /// If [future] completes with an error, the returned future completes with + /// the same error. static Future release(Future future) { return future.then(_release); } - /** - * Capture the results of a stream into a stream of [Result] values. - * - * The returned stream will not have any error events. - * Errors from the source stream have been converted to [ErrorResult]s. - * - * Shorthand for transforming the stream using [CaptureStreamTransformer]. - */ + /// Capture the results of a stream into a stream of [Result] values. + /// + /// The returned stream will not have any error events. + /// Errors from the source stream have been converted to [ErrorResult]s. + /// + /// Shorthand for transforming the stream using [CaptureStreamTransformer]. static Stream captureStream(Stream source) { return source.transform(const CaptureStreamTransformer()); } - /** - * Release a stream of [result] values into a stream of the results. - * - * `Result` values of the source stream become value or error events in - * the returned stream as appropriate. - * Errors from the source stream become errors in the returned stream. - * - * Shorthand for transforming the stream using [ReleaseStreamTransformer]. - */ + /// Release a stream of [result] values into a stream of the results. + /// + /// `Result` values of the source stream become value or error events in + /// the returned stream as appropriate. + /// Errors from the source stream become errors in the returned stream. + /// + /// Shorthand for transforming the stream using [ReleaseStreamTransformer]. static Stream releaseStream(Stream source) { return source.transform(const ReleaseStreamTransformer()); } - /** - * Converts a result of a result to a single result. - * - * If the result is an error, or it is a `Result` value - * which is then an error, then a result with that error is returned. - * Otherwise both levels of results are value results, and a single - * result with the value is returned. - */ + /// Converts a result of a result to a single result. + /// + /// If the result is an error, or it is a `Result` value + /// which is then an error, then a result with that error is returned. + /// Otherwise both levels of results are value results, and a single + /// result with the value is returned. static Result flatten(Result result) { if (result.isError) return result; return result.asValue.value; } - /** - * Whether this result is a value result. - * - * Always the opposite of [isError]. - */ + /// Whether this result is a value result. + /// + /// Always the opposite of [isError]. bool get isValue; - /** - * Whether this result is an error result. - * - * Always the opposite of [isValue]. - */ + /// Whether this result is an error result. + /// + /// Always the opposite of [isValue]. bool get isError; - /** - * If this is a value result, return itself. - * - * Otherwise return `null`. - */ + /// If this is a value result, return itself. + /// + /// Otherwise return `null`. ValueResult get asValue; - /** - * If this is an error result, return itself. - * - * Otherwise return `null`. - */ + /// If this is an error result, return itself. + /// + /// Otherwise return `null`. ErrorResult get asError; - /** - * Complete a completer with this result. - */ + /// Complete a completer with this result. void complete(Completer completer); - /** - * Add this result to a [StreamSink]. - */ + /// Add this result to an [EventSink]. + /// + /// Calls the sink's `add` or `addError` method as appropriate. void addTo(EventSink sink); - /** - * Creates a future completed with this result as a value or an error. - */ + /// Creates a future completed with this result as a value or an error. Future get asFuture; } -/** - * A result representing a returned value. - */ +/// A result representing a returned value. class ValueResult implements Result { - /** The returned value that this result represents. */ final T value; - /** Create a value result with the given [value]. */ ValueResult(this.value); bool get isValue => true; bool get isError => false; @@ -184,15 +148,10 @@ class ValueResult implements Result { Future get asFuture => new Future.value(value); } -/** - * A result representing a thrown error. - */ +/// A result representing a thrown error. class ErrorResult implements Result { - /** The thrown object that this result represents. */ final error; - /** The stack trace, if any, associated with the throw. */ final StackTrace stackTrace; - /** Create an error result with the given [error] and [stackTrace]. */ ErrorResult(this.error, this.stackTrace); bool get isValue => false; bool get isError => true; @@ -205,14 +164,26 @@ class ErrorResult implements Result { sink.addError(error, stackTrace); } Future get asFuture => new Future.error(error, stackTrace); + + /// Calls an error handler with the error and stacktrace. + /// + /// An async error handler function is either a function expecting two + /// arguments, which will be called with the error and the stack trace, + /// or it has to be a function expecting only one argument, + /// which will be called with only the error. + void handle(Function errorHandler) { + if (errorHandler is ZoneBinaryCallback) { + errorHandler(error, stackTrace); + } else { + errorHandler(error); + } + } } -/** - * A stream transformer that captures a stream of events into [Result]s. - * - * The result of the transformation is a stream of [Result] values and - * no error events. - */ +/// A stream transformer that captures a stream of events into [Result]s. +/// +/// The result of the transformation is a stream of [Result] values and +/// no error events. class CaptureStreamTransformer implements StreamTransformer> { const CaptureStreamTransformer(); @@ -225,12 +196,10 @@ class CaptureStreamTransformer implements StreamTransformer> { } } -/** - * A stream transformer that releases a stream of result events. - * - * The result of the transformation is a stream of values and - * error events. - */ +/// A stream transformer that releases a stream of result events. +/// +/// The result of the transformation is a stream of values and +/// error events. class ReleaseStreamTransformer implements StreamTransformer, T> { const ReleaseStreamTransformer(); @@ -243,15 +212,13 @@ class ReleaseStreamTransformer implements StreamTransformer, T> { } } -/** - * An event sink wrapper that captures the incoming events. - * - * Wraps an [EventSink] that expects [Result] values. - * Accepts any value and error result, - * and passes them to the wrapped sink as [Result] values. - * - * The wrapped sink will never receive an error event. - */ +/// An event sink wrapper that captures the incoming events. +/// +/// Wraps an [EventSink] that expects [Result] values. +/// Accepts any value and error result, +/// and passes them to the wrapped sink as [Result] values. +/// +/// The wrapped sink will never receive an error event. class CaptureSink implements EventSink { final EventSink _sink; @@ -263,13 +230,11 @@ class CaptureSink implements EventSink { void close() { _sink.close(); } } -/** - * An event sink wrapper that releases the incoming result events. - * - * Wraps an output [EventSink] that expects any result. - * Accepts [Result] values, and puts the result value or error into the - * corresponding output sink add method. - */ +/// An event sink wrapper that releases the incoming result events. +/// +/// Wraps an output [EventSink] that expects any result. +/// Accepts [Result] values, and puts the result value or error into the +/// corresponding output sink add method. class ReleaseSink implements EventSink> { final EventSink _sink; ReleaseSink(EventSink sink) : _sink = sink; diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 656d5638..848c4552 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -257,6 +257,54 @@ void main() { new Result>.value(new Result.value(42)); expectResult(Result.flatten(result), new Result.value(42)); }); + + test("handle unary", () { + var result = new Result.error("error", stack); + bool called = false; + result.handle((error) { + called = true; + expect(error, "error"); + }); + expect(called, isTrue); + }); + + test("handle binary", () { + var result = new Result.error("error", stack); + bool called = false; + result.handle((error, stackTrace) { + called = true; + expect(error, "error"); + expect(stackTrace, same(stack)); + }); + expect(called, isTrue); + }); + + test("handle unary and binary", () { + var result = new Result.error("error", stack); + bool called = false; + result.handle((error, [stackTrace]) { + called = true; + expect(error, "error"); + expect(stackTrace, same(stack)); + }); + expect(called, isTrue); + }); + + test("handle neither unary nor binary", () { + var result = new Result.error("error", stack); + expect(() => result.handle(() => fail("unreachable")), + throws); + expect(() => result.handle((a, b, c) => fail("unreachable")), + throws); + expect(() => result.handle((a, b, {c}) => fail("unreachable")), + throws); + expect(() => result.handle((a, {b}) => fail("unreachable")), + throws); + expect(() => result.handle(({a, b}) => fail("unreachable")), + throws); + expect(() => result.handle(({a}) => fail("unreachable")), + throws); + }); } void expectResult(Result actual, Result expected) { From cc0ba58793a5c8d3b87ac235ce1f4c0faea25c58 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 6 Jul 2015 13:24:36 -0700 Subject: [PATCH 032/260] Add FutureGroup.onIdle and FutureGroup.isIdle. R=lrn@google.com Review URL: https://codereview.chromium.org//1215063008. --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/src/future_group.dart | 23 ++++- pkgs/async/test/future_group_test.dart | 116 ++++++++++++++++++++++--- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index ab118014..8b232aa5 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -14,6 +14,9 @@ - Added `SubscriptionStream` which creates a single-subscription stream from an existing stream subscription. +- Added `FutureGroup.onIdle` and `FutureGroup.isIdle`, which provide visibility + into whether a group is actively waiting on any futures. + ## 1.2.0 - Added a `FutureGroup` class for waiting for a group of futures, potentially of diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 81ba4fae..02ff185c 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -35,6 +35,22 @@ class FutureGroup implements Sink> { Future> get future => _completer.future; final _completer = new Completer>(); + /// Whether this group is waiting on any futures. + bool get isIdle => _pending == 0; + + /// A broadcast stream that emits a `null` event whenever the last pending + /// future in this group completes. + /// + /// Once this group isn't waiting on any futures *and* [close] has been + /// called, this stream will close. + Stream get onIdle { + if (_onIdleController == null) { + _onIdleController = new StreamController.broadcast(sync: true); + } + return _onIdleController.stream; + } + StreamController _onIdleController; + /// The values emitted by the futures that have been added to the group, in /// the order they were added. /// @@ -58,7 +74,12 @@ class FutureGroup implements Sink> { _pending--; _values[index] = value; - if (_pending == 0 && _closed) _completer.complete(_values); + if (_pending != 0) return; + if (_onIdleController != null) _onIdleController.add(null); + + if (!_closed) return; + if (_onIdleController != null) _onIdleController.close(); + _completer.complete(_values); }).catchError((error, stackTrace) { if (_completer.isCompleted) return; _completer.completeError(error, stackTrace); diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index f5350805..09ea29a0 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -7,6 +7,8 @@ import 'dart:async'; import 'package:async/src/future_group.dart'; import 'package:test/test.dart'; +import 'utils.dart'; + void main() { var futureGroup; setUp(() { @@ -18,7 +20,7 @@ void main() { var completed = false; futureGroup.future.then((_) => completed = true); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); expect(completed, isFalse); }); @@ -31,18 +33,18 @@ void main() { group("with a future that already completed", () { test("never completes if nothing happens", () async { futureGroup.add(new Future.value()); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); var completed = false; futureGroup.future.then((_) => completed = true); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); expect(completed, isFalse); }); test("completes once it's closed", () async { futureGroup.add(new Future.value()); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); expect(futureGroup.future, completes); futureGroup.close(); @@ -61,7 +63,6 @@ void main() { }); test("completes once all contained futures complete", () async { - var futureGroup = new FutureGroup(); var completer1 = new Completer(); var completer2 = new Completer(); var completer3 = new Completer(); @@ -75,20 +76,19 @@ void main() { futureGroup.future.then((_) => completed = true); completer1.complete(); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); expect(completed, isFalse); completer2.complete(); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); expect(completed, isFalse); completer3.complete(); - await new Future.delayed(Duration.ZERO); + await flushMicrotasks(); expect(completed, isTrue); }); test("completes to the values of the futures in order of addition", () { - var futureGroup = new FutureGroup(); var completer1 = new Completer(); var completer2 = new Completer(); var completer3 = new Completer(); @@ -108,7 +108,6 @@ void main() { test("completes to the first error to be emitted, even if it's not closed", () { - var futureGroup = new FutureGroup(); var completer1 = new Completer(); var completer2 = new Completer(); var completer3 = new Completer(); @@ -121,4 +120,101 @@ void main() { completer1.completeError("error 1"); expect(futureGroup.future, throwsA("error 2")); }); + + group("onIdle:", () { + test("emits an event when the last pending future completes", () async { + var idle = false; + futureGroup.onIdle.listen((_) => idle = true); + + var completer1 = new Completer(); + var completer2 = new Completer(); + var completer3 = new Completer(); + + futureGroup.add(completer1.future); + futureGroup.add(completer2.future); + futureGroup.add(completer3.future); + + await flushMicrotasks(); + expect(idle, isFalse); + expect(futureGroup.isIdle, isFalse); + + completer1.complete(); + await flushMicrotasks(); + expect(idle, isFalse); + expect(futureGroup.isIdle, isFalse); + + completer2.complete(); + await flushMicrotasks(); + expect(idle, isFalse); + expect(futureGroup.isIdle, isFalse); + + completer3.complete(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(futureGroup.isIdle, isTrue); + }); + + test("emits an event each time it becomes idle", () async { + var idle = false; + futureGroup.onIdle.listen((_) => idle = true); + + var completer = new Completer(); + futureGroup.add(completer.future); + + completer.complete(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(futureGroup.isIdle, isTrue); + + idle = false; + completer = new Completer(); + futureGroup.add(completer.future); + + await flushMicrotasks(); + expect(idle, isFalse); + expect(futureGroup.isIdle, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(futureGroup.isIdle, isTrue); + }); + + test("emits an event when the group closes", () async { + // It's important that the order of events here stays consistent over + // time, since code may rely on it in subtle ways. + var idle = false; + var onIdleDone = false; + var futureFired = false; + + futureGroup.onIdle.listen(expectAsync((_) { + expect(futureFired, isFalse); + idle = true; + }), onDone: expectAsync(() { + expect(idle, isTrue); + expect(futureFired, isFalse); + onIdleDone = true; + })); + + futureGroup.future.then(expectAsync((_) { + expect(idle, isTrue); + expect(onIdleDone, isTrue); + futureFired = true; + })); + + var completer = new Completer(); + futureGroup.add(completer.future); + futureGroup.close(); + + await flushMicrotasks(); + expect(idle, isFalse); + expect(futureGroup.isIdle, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(futureGroup.isIdle, isTrue); + expect(futureFired, isTrue); + }); + }); } From 07ff730094219307f69fe54247e16c00e7fa24f8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 7 Jul 2015 14:52:50 -0700 Subject: [PATCH 033/260] Add a bunch of delegates for dart:async types. A number of these are wrappers I've needed in the past or know I'll need in the future; some are just for good measure. R=lrn@google.com Review URL: https://codereview.chromium.org//1219493008. --- pkgs/async/CHANGELOG.md | 11 ++++--- pkgs/async/lib/async.dart | 9 ++++-- pkgs/async/lib/src/delegate/event_sink.dart | 25 ++++++++++++++++ pkgs/async/lib/src/delegate/future.dart | 28 +++++++++++++++++ pkgs/async/lib/src/delegate/sink.dart | 21 +++++++++++++ .../lib/src/delegate/stream_consumer.dart | 23 ++++++++++++++ pkgs/async/lib/src/delegate/stream_sink.dart | 30 +++++++++++++++++++ .../stream_subscription.dart} | 2 +- pkgs/async/lib/src/subscription_stream.dart | 2 +- 9 files changed, 143 insertions(+), 8 deletions(-) create mode 100644 pkgs/async/lib/src/delegate/event_sink.dart create mode 100644 pkgs/async/lib/src/delegate/future.dart create mode 100644 pkgs/async/lib/src/delegate/sink.dart create mode 100644 pkgs/async/lib/src/delegate/stream_consumer.dart create mode 100644 pkgs/async/lib/src/delegate/stream_sink.dart rename pkgs/async/lib/src/{delegating_stream_subscription.dart => delegate/stream_subscription.dart} (96%) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 8b232aa5..2a83e3f4 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -7,16 +7,19 @@ before they are avilable. It is like a `StreamIterator` that can queue requests. -- Added `DelegatingStreamSubscription` which is a simple wrapper around - a `StreamSubscription` that forwards all call to the wrapped subscription. - It can be extended to wrap extra functionality around a subscription. - - Added `SubscriptionStream` which creates a single-subscription stream from an existing stream subscription. - Added `FutureGroup.onIdle` and `FutureGroup.isIdle`, which provide visibility into whether a group is actively waiting on any futures. +- Added delegating wrapper classes for a number of core async types: + `DelegatingFuture`, `DelegatingStreamConsumer`, `DelegatingStreamController`, + `DelegatingSink`, `DelegatingEventSink`, `DelegatingStreamSink`, and + `DelegatingStreamSubscription`. These are all simple wrappers that forward all + calls to the wrapped objects. They can be used to expose only the desired + interface for subclasses, or extended to add extra functionality. + ## 1.2.0 - Added a `FutureGroup` class for waiting for a group of futures, potentially of diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index fc36d435..35b28e07 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -4,12 +4,17 @@ library dart.pkg.async; -export "stream_zip.dart"; export "result.dart"; -export "src/delegating_stream_subscription.dart"; +export "src/delegate/event_sink.dart"; +export "src/delegate/future.dart"; +export "src/delegate/sink.dart"; +export "src/delegate/stream_consumer.dart"; +export "src/delegate/stream_sink.dart"; +export "src/delegate/stream_subscription.dart"; export "src/future_group.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; export "src/stream_queue.dart"; export "src/stream_splitter.dart"; export "src/subscription_stream.dart"; +export "stream_zip.dart"; diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart new file mode 100644 index 00000000..1fe86c15 --- /dev/null +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.delegate.event_sink; + +import 'dart:async'; + +/// Simple delegating wrapper around an [EventSink]. +/// +/// Subclasses can override individual methods, or use this to expose only the +/// [EventSink] methods of a subclass. +class DelegatingEventSink implements EventSink { + final EventSink _sink; + + /// Create a delegating sink forwarding calls to [sink]. + DelegatingEventSink(EventSink sink) : _sink = sink; + + void add(T data) => _sink.add(data); + + void addError(error, [StackTrace stackTrace]) => + _sink.addError(error, stackTrace); + + void close() => _sink.close(); +} diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart new file mode 100644 index 00000000..34f61589 --- /dev/null +++ b/pkgs/async/lib/src/delegate/future.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.delegate.future; + +import 'dart:async'; + +/// A wrapper that forwards calls to a [Future]. +class DelegatingFuture implements Future { + /// The wrapped [Future]. + final Future _future; + + DelegatingFuture(this._future); + + Stream asStream() => _future.asStream(); + + Future catchError(Function onError, {bool test(error)}) => + _future.catchError(onError, test: test); + + Future then(onValue(T value), {Function onError}) => + _future.then(onValue, onError: onError); + + Future whenComplete(action()) => _future.whenComplete(action); + + Future timeout(Duration timeLimit, {void onTimeout()}) => + _future.timeout(timeLimit, onTimeout: onTimeout); +} diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart new file mode 100644 index 00000000..5cf4f563 --- /dev/null +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -0,0 +1,21 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.delegate.sink; + +/// Simple delegating wrapper around a [Sink]. +/// +/// Subclasses can override individual methods, or use this to expose only the +/// [Sink] methods of a subclass. +class DelegatingSink implements Sink { + final Sink _sink; + + /// Create a delegating sink forwarding calls to [sink]. + DelegatingSink(Sink sink) + : _sink = sink; + + void add(T data) => _sink.add(data); + + void close() => _sink.close(); +} diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart new file mode 100644 index 00000000..8162db6d --- /dev/null +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.delegate.stream_consumer; + +import 'dart:async'; + +/// Simple delegating wrapper around a [StreamConsumer]. +/// +/// Subclasses can override individual methods, or use this to expose only the +/// [StreamConsumer] methods of a subclass. +class DelegatingStreamConsumer implements StreamConsumer { + final StreamConsumer _consumer; + + /// Create a delegating consumer forwarding calls to [consumer]. + DelegatingStreamConsumer(StreamConsumer consumer) + : _consumer = consumer; + + Future addStream(Stream stream) => _consumer.addStream(stream); + + Future close() => _consumer.close(); +} diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart new file mode 100644 index 00000000..9742b4ce --- /dev/null +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.delegate.stream_sink; + +import 'dart:async'; + +/// Simple delegating wrapper around a [StreamSink]. +/// +/// Subclasses can override individual methods, or use this to expose only the +/// [StreamSink] methods of a subclass. +class DelegatingStreamSink implements StreamSink { + final StreamSink _sink; + + Future get done => _sink.done; + + /// Create delegating sink forwarding calls to [sink]. + DelegatingStreamSink(StreamSink sink) + : _sink = sink; + + void add(T data) => _sink.add(data); + + void addError(error, [StackTrace stackTrace]) => + _sink.addError(error, stackTrace); + + Future addStream(Stream stream) => _sink.addStream(stream); + + Future close() => _sink.close(); +} diff --git a/pkgs/async/lib/src/delegating_stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart similarity index 96% rename from pkgs/async/lib/src/delegating_stream_subscription.dart rename to pkgs/async/lib/src/delegate/stream_subscription.dart index 87629e5a..ff9b6658 100644 --- a/pkgs/async/lib/src/delegating_stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegating_stream_subscription; +library async.delegate.stream_subscription; import 'dart:async'; diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index a1d533f8..e9e7d77b 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -6,7 +6,7 @@ library async.subscription_stream; import 'dart:async'; -import "delegating_stream_subscription.dart"; +import "delegate/stream_subscription.dart"; /// A [Stream] adapter for a [StreamSubscription]. /// From ee89638db768fd505b75f637dd5dbee44ba5f256 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 8 Jul 2015 13:57:16 -0700 Subject: [PATCH 034/260] Use a List rather than a Queue in StreamSplitter. R=lrn@google.com Review URL: https://codereview.chromium.org//1205143002 . --- pkgs/async/lib/src/stream_splitter.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index addba934..6ec440f5 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -35,7 +35,7 @@ class StreamSplitter { /// The buffer of events or errors that have already been emitted by /// [_stream]. - final _buffer = new Queue>(); + final _buffer = new List>(); /// The controllers for branches that are listening for future events from /// [_stream]. From 618c4b5a80c99324f5d129c3da0b4b874e241464 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 8 Jul 2015 17:45:21 -0700 Subject: [PATCH 035/260] Add an AsyncThunk class. R=lrn@google.com Review URL: https://codereview.chromium.org//1220963002 . --- pkgs/async/CHANGELOG.md | 3 ++ pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/async_memoizer.dart | 47 ++++++++++++++++++++++++ pkgs/async/test/async_memoizer_test.dart | 37 +++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 pkgs/async/lib/src/async_memoizer.dart create mode 100644 pkgs/async/test/async_memoizer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 2a83e3f4..dcd36cf4 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -13,6 +13,9 @@ - Added `FutureGroup.onIdle` and `FutureGroup.isIdle`, which provide visibility into whether a group is actively waiting on any futures. +- Add an `AsyncMemoizer` class for running an asynchronous block of code exactly + once. + - Added delegating wrapper classes for a number of core async types: `DelegatingFuture`, `DelegatingStreamConsumer`, `DelegatingStreamController`, `DelegatingSink`, `DelegatingEventSink`, `DelegatingStreamSink`, and diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 35b28e07..97a574b8 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -5,6 +5,7 @@ library dart.pkg.async; export "result.dart"; +export "src/async_memoizer.dart"; export "src/delegate/event_sink.dart"; export "src/delegate/future.dart"; export "src/delegate/sink.dart"; diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart new file mode 100644 index 00000000..68a6c80e --- /dev/null +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -0,0 +1,47 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.async_memoizer; + +import 'dart:async'; + +/// A class for running an asynchronous function exactly once and caching its +/// result. +/// +/// An `AsyncMemoizer` is used when some function may be run multiple times in +/// order to get its result, but it only actually needs to be run once for its +/// effect. To memoize the result of an async function, you can create a +/// memoizer outside the function (for example as an instance field if you want +/// to memoize the result of a method), and then wrap the function's body in a +/// call to [runOnce]. +/// +/// This is useful for methods like `close()` and getters that need to do +/// asynchronous work. For example: +/// +/// ```dart +/// class SomeResource { +/// final _closeMemo = new AsyncMemoizer(); +/// +/// Future close() => _closeMemo.runOnce(() { +/// // ... +/// }); +/// } +/// ``` +class AsyncMemoizer { + /// The future containing the method's result. + /// + /// This will be `null` if [run] hasn't been called yet. + Future _future; + + /// Whether [run] has been called yet. + bool get hasRun => _future != null; + + /// Runs the function, [computation], if it hasn't been run before. + /// + /// If [run] has already been called, this returns the original result. + Future runOnce(computation()) { + if (_future == null) _future = new Future.sync(computation); + return _future; + } +} diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart new file mode 100644 index 00000000..3289aa96 --- /dev/null +++ b/pkgs/async/test/async_memoizer_test.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +main() { + var cache; + setUp(() => cache = new AsyncMemoizer()); + + test("runs the function only the first time runOnce() is called", () async { + var count = 0; + expect(await cache.runOnce(() => count++), equals(0)); + expect(count, equals(1)); + + expect(await cache.runOnce(() => count++), equals(0)); + expect(count, equals(1)); + }); + + test("forwards the return value from the function", () async { + expect(cache.runOnce(() => "value"), completion(equals("value"))); + expect(cache.runOnce(() {}), completion(equals("value"))); + }); + + test("forwards the return value from an async function", () async { + expect(cache.runOnce(() async => "value"), completion(equals("value"))); + expect(cache.runOnce(() {}), completion(equals("value"))); + }); + + test("forwards the error from an async function", () async { + expect(cache.runOnce(() async => throw "error"), throwsA("error")); + expect(cache.runOnce(() {}), throwsA("error")); + }); +} From 908a9001f19d71dafec11eadeb49861c1690ba51 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 8 Jul 2015 17:52:44 -0700 Subject: [PATCH 036/260] Add an [immediate] argument to StreamQueue.cancel. Canceling the subscription immediately is useful for situations where the normal flow of a program is interrupted, for example by a signal on the command-line. R=lrn@google.com Review URL: https://codereview.chromium.org//1221713005 . --- pkgs/async/lib/src/stream_queue.dart | 26 ++++++++++-- pkgs/async/test/stream_queue_test.dart | 55 +++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 0018710b..53ed1421 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -91,7 +91,7 @@ class StreamQueue { /// Whether a closing operation has been performed on the stream queue. /// - /// Closing operations are [cancel] and [rest]. + /// Closing operations are [cancel], [cancelImmediately], and [rest]. bool _isClosed = false; /// Queue of events not used by a request yet. @@ -238,10 +238,31 @@ class StreamQueue { throw _failClosed(); } + /// Cancels the underlying stream subscription immediately. + /// + /// Any pending events will complete as though the stream had closed when + /// [cancel] was called. + /// + /// The returned future completes with the result of calling + /// `StreamSubscription.cancel`. + /// + /// After calling `cancelImmediately`, no further events can be requested. + /// None of [next], [rest], [skip], [take] or [cancel] may be called again. + Future cancelImmediately() { + if (_isClosed) throw _failClosed(); + _isClosed = true; + + if (_isDone) return new Future.value(); + if (_subscription == null) _subscription = _sourceStream.listen(null); + var future = _subscription.cancel(); + _onDone(); + return future; + } + /// Returns an error for when a request is made after cancel. /// /// Returns a [StateError] with a message saying that either - /// [cancel] or [rest] have already been called. + /// [cancel], [cancelImmediately], or [rest] have already been called. Error _failClosed() { return new StateError("Already cancelled"); } @@ -280,7 +301,6 @@ class StreamQueue { _ensureListening(); } _requestQueue.add(request); - } /// Ensures that we are listening on events from [_sourceStream]. diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index e5fe25cf..dae8ed26 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -333,7 +333,7 @@ main() { }); }); - group("close operation", () { + group("cancel operation", () { test("closes the events, prevents any other operation", () async { var events = new StreamQueue(createStream()); await events.cancel(); @@ -354,6 +354,59 @@ main() { }); }); + group("cancelImmediately()", () async { + test("closes the events, prevents any other operation", () async { + var events = new StreamQueue(createStream()); + await events.cancelImmediately(); + expect(() => events.next, throwsStateError); + expect(() => events.skip(1), throwsStateError); + expect(() => events.take(1), throwsStateError); + expect(() => events.rest, throwsStateError); + expect(() => events.cancel(), throwsStateError); + }); + + test("cancels the underlying subscription immediately", () async { + var controller = new StreamController(); + controller.add(1); + + var events = new StreamQueue(controller.stream); + expect(await events.next, 1); + expect(controller.hasListener, isTrue); + + events.cancelImmediately(); + await expect(controller.hasListener, isFalse); + }); + + test("closes pending requests", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(events.next, throwsStateError); + expect(events.hasNext, completion(isFalse)); + + await events.cancelImmediately(); + }); + + test("returns the result of closing the underlying subscription", () async { + var controller = new StreamController( + onCancel: () => new Future.value(42)); + var events = new StreamQueue(controller.stream); + expect(await events.cancelImmediately(), 42); + }); + + test("listens and then cancels a stream that hasn't been listened to yet", + () async { + var wasListened = false; + var controller = new StreamController( + onListen: () => wasListened = true); + var events = new StreamQueue(controller.stream); + expect(wasListened, isFalse); + expect(controller.hasListener, isFalse); + + await events.cancelImmediately(); + expect(wasListened, isTrue); + expect(controller.hasListener, isFalse); + }); + }); group("hasNext operation", () { test("true at start", () async { From 409b2dfd1b0eddfa2772924884bf41274377fe85 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 13 Jul 2015 13:06:22 -0700 Subject: [PATCH 037/260] Added a `ResultFuture` class. This allows code to synchronously access the result of a wrapped future. R=lrn@google.com Review URL: https://codereview.chromium.org//1214843004 . --- pkgs/async/CHANGELOG.md | 3 ++ pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/result_future.dart | 35 ++++++++++++++++++++ pkgs/async/test/result_future_test.dart | 44 +++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 pkgs/async/lib/src/result_future.dart create mode 100644 pkgs/async/test/result_future_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index dcd36cf4..39d204b2 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -10,6 +10,9 @@ - Added `SubscriptionStream` which creates a single-subscription stream from an existing stream subscription. +- Added a `ResultFuture` class for synchronously accessing the result of a + wrapped future. + - Added `FutureGroup.onIdle` and `FutureGroup.isIdle`, which provide visibility into whether a group is actively waiting on any futures. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 97a574b8..71a18492 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -13,6 +13,7 @@ export "src/delegate/stream_consumer.dart"; export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; export "src/future_group.dart"; +export "src/result_future.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; export "src/stream_queue.dart"; diff --git a/pkgs/async/lib/src/result_future.dart b/pkgs/async/lib/src/result_future.dart new file mode 100644 index 00000000..311e83fe --- /dev/null +++ b/pkgs/async/lib/src/result_future.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.result_future; + +import 'dart:async'; + +import '../result.dart'; +import 'delegate/future.dart'; + +/// A [Future] wrapper that provides synchronous access to the result of the +/// wrapped [Future] once it's completed. +class ResultFuture extends DelegatingFuture { + /// Whether the future has fired and [result] is available. + bool get isComplete => result != null; + + /// The result of the wrapped [Future], if it's completed. + /// + /// If it hasn't completed yet, this will be `null`. + Result get result => _result; + Result _result; + + factory ResultFuture(Future future) { + var resultFuture; + resultFuture = new ResultFuture._(Result.capture(future).then((result) { + resultFuture._result = result; + return result.asFuture; + })); + return resultFuture; + } + + ResultFuture._(Future future) + : super(future); +} diff --git a/pkgs/async/test/result_future_test.dart b/pkgs/async/test/result_future_test.dart new file mode 100644 index 00000000..d8b4e01e --- /dev/null +++ b/pkgs/async/test/result_future_test.dart @@ -0,0 +1,44 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:stack_trace/stack_trace.dart'; +import 'package:test/test.dart'; + +void main() { + var completer; + var future; + setUp(() { + completer = new Completer(); + future = new ResultFuture(completer.future); + }); + + test('before completion, result is null', () { + expect(future.result, isNull); + }); + + test('after successful completion, result is the value of the future', () { + completer.complete(12); + + // The completer calls its listeners asynchronously. We have to wait + // before we can access the result. + expect(future.then((_) => future.result.asValue.value), + completion(equals(12))); + }); + + test("after an error completion, result is the future's error", () { + var trace = new Trace.current(); + completer.completeError('error', trace); + + // The completer calls its listeners asynchronously. We have to wait + // before we can access the result. + return future.catchError((_) {}).then((_) { + var error = future.result.asError; + expect(error.error, equals('error')); + expect(error.stackTrace, equals(trace)); + }); + }); +} From 19c14a8c4b35a094980c61c05f754f504cf0466a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 13 Jul 2015 13:15:51 -0700 Subject: [PATCH 038/260] Use a named param for StreamQueue.cancelImmediately. R=lrn@google.com Review URL: https://codereview.chromium.org//1223423002 . --- pkgs/async/lib/src/stream_queue.dart | 38 ++++----- pkgs/async/test/stream_queue_test.dart | 105 +++++++++++++------------ 2 files changed, 67 insertions(+), 76 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 53ed1421..7d78ac59 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -91,7 +91,7 @@ class StreamQueue { /// Whether a closing operation has been performed on the stream queue. /// - /// Closing operations are [cancel], [cancelImmediately], and [rest]. + /// Closing operations are [cancel] and [rest]. bool _isClosed = false; /// Queue of events not used by a request yet. @@ -218,9 +218,13 @@ class StreamQueue { /// Cancels the underlying stream subscription. /// - /// The cancel operation waits until all previously requested - /// events have been processed, then it cancels the subscription - /// providing the events. + /// If [immediate] is `false` (the default), the cancel operation waits until + /// all previously requested events have been processed, then it cancels the + /// subscription providing the events. + /// + /// If [immediate] is `true`, the subscription is instead canceled + /// immediately. Any pending events are completed as though the underlying + /// stream had closed. /// /// The returned future completes with the result of calling /// `cancel`. @@ -228,29 +232,15 @@ class StreamQueue { /// After calling `cancel`, no further events can be requested. /// None of [next], [rest], [skip], [take] or [cancel] may be /// called again. - Future cancel() { - if (!_isClosed) { - _isClosed = true; + Future cancel({bool immediate: false}) { + if (_isClosed) throw _failClosed(); + _isClosed = true; + + if (!immediate) { var request = new _CancelRequest(this); _addRequest(request); return request.future; } - throw _failClosed(); - } - - /// Cancels the underlying stream subscription immediately. - /// - /// Any pending events will complete as though the stream had closed when - /// [cancel] was called. - /// - /// The returned future completes with the result of calling - /// `StreamSubscription.cancel`. - /// - /// After calling `cancelImmediately`, no further events can be requested. - /// None of [next], [rest], [skip], [take] or [cancel] may be called again. - Future cancelImmediately() { - if (_isClosed) throw _failClosed(); - _isClosed = true; if (_isDone) return new Future.value(); if (_subscription == null) _subscription = _sourceStream.listen(null); @@ -262,7 +252,7 @@ class StreamQueue { /// Returns an error for when a request is made after cancel. /// /// Returns a [StateError] with a message saying that either - /// [cancel], [cancelImmediately], or [rest] have already been called. + /// [cancel] or [rest] have already been called. Error _failClosed() { return new StateError("Already cancelled"); } diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index dae8ed26..cad0ad51 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -352,59 +352,60 @@ main() { expect(await events.next, 1); expect(await events.cancel(), 42); }); - }); - - group("cancelImmediately()", () async { - test("closes the events, prevents any other operation", () async { - var events = new StreamQueue(createStream()); - await events.cancelImmediately(); - expect(() => events.next, throwsStateError); - expect(() => events.skip(1), throwsStateError); - expect(() => events.take(1), throwsStateError); - expect(() => events.rest, throwsStateError); - expect(() => events.cancel(), throwsStateError); - }); - - test("cancels the underlying subscription immediately", () async { - var controller = new StreamController(); - controller.add(1); - - var events = new StreamQueue(controller.stream); - expect(await events.next, 1); - expect(controller.hasListener, isTrue); - - events.cancelImmediately(); - await expect(controller.hasListener, isFalse); - }); - - test("closes pending requests", () async { - var events = new StreamQueue(createStream()); - expect(await events.next, 1); - expect(events.next, throwsStateError); - expect(events.hasNext, completion(isFalse)); - - await events.cancelImmediately(); - }); - - test("returns the result of closing the underlying subscription", () async { - var controller = new StreamController( - onCancel: () => new Future.value(42)); - var events = new StreamQueue(controller.stream); - expect(await events.cancelImmediately(), 42); - }); - test("listens and then cancels a stream that hasn't been listened to yet", - () async { - var wasListened = false; - var controller = new StreamController( - onListen: () => wasListened = true); - var events = new StreamQueue(controller.stream); - expect(wasListened, isFalse); - expect(controller.hasListener, isFalse); - - await events.cancelImmediately(); - expect(wasListened, isTrue); - expect(controller.hasListener, isFalse); + group("with immediate: true", () async { + test("closes the events, prevents any other operation", () async { + var events = new StreamQueue(createStream()); + await events.cancel(immediate: true); + expect(() => events.next, throwsStateError); + expect(() => events.skip(1), throwsStateError); + expect(() => events.take(1), throwsStateError); + expect(() => events.rest, throwsStateError); + expect(() => events.cancel(), throwsStateError); + }); + + test("cancels the underlying subscription immediately", () async { + var controller = new StreamController(); + controller.add(1); + + var events = new StreamQueue(controller.stream); + expect(await events.next, 1); + expect(controller.hasListener, isTrue); + + events.cancel(immediate: true); + await expect(controller.hasListener, isFalse); + }); + + test("closes pending requests", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(events.next, throwsStateError); + expect(events.hasNext, completion(isFalse)); + + await events.cancel(immediate: true); + }); + + test("returns the result of closing the underlying subscription", + () async { + var controller = new StreamController( + onCancel: () => new Future.value(42)); + var events = new StreamQueue(controller.stream); + expect(await events.cancel(immediate: true), 42); + }); + + test("listens and then cancels a stream that hasn't been listened to yet", + () async { + var wasListened = false; + var controller = new StreamController( + onListen: () => wasListened = true); + var events = new StreamQueue(controller.stream); + expect(wasListened, isFalse); + expect(controller.hasListener, isFalse); + + await events.cancel(immediate: true); + expect(wasListened, isTrue); + expect(controller.hasListener, isFalse); + }); }); }); From 8141bbb394fc84c2c326585cbb8d73702c180a59 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 Jul 2015 13:36:01 -0700 Subject: [PATCH 039/260] Fix a bug in StreamQueue.cancel(). If cancel was called on the queue before anything else, it wouldn't call StreamSubscription.cancel. R=lrn@google.com Review URL: https://codereview.chromium.org//1239543004 . --- pkgs/async/lib/src/stream_queue.dart | 3 ++- pkgs/async/test/stream_queue_test.dart | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 7d78ac59..e4b3b640 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -530,9 +530,10 @@ class _CancelRequest implements _EventRequest { } void _shutdown() { - if (_streamQueue._subscription == null) { + if (_streamQueue._isDone) { _completer.complete(); } else { + _streamQueue._ensureListening(); _completer.complete(_streamQueue._dispose().cancel()); } } diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index cad0ad51..228ba8ad 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -344,6 +344,14 @@ main() { expect(() => events.cancel(), throwsStateError); }); + test("cancels underlying subscription when called before any event", + () async { + var cancelFuture = new Future.value(42); + var controller = new StreamController(onCancel: () => cancelFuture); + var events = new StreamQueue(controller.stream); + expect(await events.cancel(), 42); + }); + test("cancels underlying subscription, returns result", () async { var cancelFuture = new Future.value(42); var controller = new StreamController(onCancel: () => cancelFuture); @@ -353,7 +361,7 @@ main() { expect(await events.cancel(), 42); }); - group("with immediate: true", () async { + group("with immediate: true", () { test("closes the events, prevents any other operation", () async { var events = new StreamQueue(createStream()); await events.cancel(immediate: true); @@ -376,6 +384,15 @@ main() { await expect(controller.hasListener, isFalse); }); + test("cancels the underlying subscription when called before any event", + () async { + var cancelFuture = new Future.value(42); + var controller = new StreamController(onCancel: () => cancelFuture); + + var events = new StreamQueue(controller.stream); + expect(await events.cancel(immediate: true), 42); + }); + test("closes pending requests", () async { var events = new StreamQueue(createStream()); expect(await events.next, 1); From d3bd45c83be76cd12d7f9b682769c1a6cc135ed5 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 Jul 2015 15:18:36 -0700 Subject: [PATCH 040/260] Add StreamQueue.fork and ForkableStream. StramQueue.fork is a very useful operation for creating complex and composable user-defined operations over stream queues. It allows arbitrary lookahead to be performed without modifying the semantics of the original stream, providing for higher-order operations like "check for this sequence of values or, if they don't exist, this other distinct sequence". Review URL: https://codereview.chromium.org//1241723003 . --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/forkable_stream.dart | 166 +++++++++ pkgs/async/lib/src/stream_queue.dart | 76 +++- pkgs/async/test/forkable_stream_test.dart | 413 +++++++++++++++++++++ pkgs/async/test/stream_queue_test.dart | 428 ++++++++++++++++++++++ 6 files changed, 1083 insertions(+), 4 deletions(-) create mode 100644 pkgs/async/lib/src/forkable_stream.dart create mode 100644 pkgs/async/test/forkable_stream_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 39d204b2..d9d85d8e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -10,6 +10,9 @@ - Added `SubscriptionStream` which creates a single-subscription stream from an existing stream subscription. +- Added `ForkableStream` which wraps a stream and allows independent forks to be + created that emit the same events as the original. + - Added a `ResultFuture` class for synchronously accessing the result of a wrapped future. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 71a18492..6cfd6e14 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -12,6 +12,7 @@ export "src/delegate/sink.dart"; export "src/delegate/stream_consumer.dart"; export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; +export "src/forkable_stream.dart"; export "src/future_group.dart"; export "src/result_future.dart"; export "src/stream_completer.dart"; diff --git a/pkgs/async/lib/src/forkable_stream.dart b/pkgs/async/lib/src/forkable_stream.dart new file mode 100644 index 00000000..bb8f4652 --- /dev/null +++ b/pkgs/async/lib/src/forkable_stream.dart @@ -0,0 +1,166 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.forkable_stream; + +import 'dart:async'; + +import 'stream_completer.dart'; + +/// A single-subscription stream from which other streams may be forked off at +/// the current position. +/// +/// This adds an operation, [fork], which produces a new stream that +/// independently emits the same events as this stream. Unlike the branches +/// produced by [StreamSplitter], a fork only emits events that arrive *after* +/// the call to [fork]. +/// +/// Each fork can be paused or canceled independently of one another and of this +/// stream. The underlying stream will be listened to once any branch is +/// listened to. It will be paused when all branches are paused or not yet +/// listened to. It will be canceled when all branches have been listened to and +/// then canceled. +class ForkableStream extends StreamView { + /// The underlying stream. + final Stream _sourceStream; + + /// The subscription to [_sourceStream]. + /// + /// This will be `null` until this stream or any of its forks are listened to. + StreamSubscription _subscription; + + /// Whether this has been cancelled and no more forks may be created. + bool _isCanceled = false; + + /// The controllers for any branches that have not yet been canceled. + /// + /// This includes a controller for this stream, until that has been cancelled. + final _controllers = new Set>(); + + /// Creates a new forkable stream wrapping [sourceStream]. + ForkableStream(Stream sourceStream) + // Use a completer here so that we can provide its stream to the + // superclass constructor while also adding the stream controller to + // [_controllers]. + : this._(sourceStream, new StreamCompleter()); + + ForkableStream._(this._sourceStream, StreamCompleter completer) + : super(completer.stream) { + completer.setSourceStream(_fork(primary: true)); + } + + /// Creates a new fork of this stream. + /// + /// From this point forward, the fork will emit the same events as this + /// stream. It will *not* emit any events that have already been emitted by + /// this stream. The fork is independent of this stream, which means each one + /// may be paused or canceled without affecting the other. + /// + /// If this stream is done or its subscription has been canceled, this returns + /// an empty stream. + Stream fork() => _fork(primary: false); + + /// Creates a stream forwarding [_sourceStream]. + /// + /// If [primary] is true, this is the stream underlying this object; + /// otherwise, it's a fork. The only difference is that when the primary + /// stream is canceled, [fork] starts throwing [StateError]s. + Stream _fork({bool primary: false}) { + if (_isCanceled) { + var controller = new StreamController()..close(); + return controller.stream; + } + + var controller; + controller = new StreamController( + onListen: () => _onListenOrResume(controller), + onCancel: () => _onCancel(controller, primary: primary), + onPause: () => _onPause(controller), + onResume: () => _onListenOrResume(controller), + sync: true); + + _controllers.add(controller); + + return controller.stream; + } + + /// The callback called when `onListen` or `onResume` is called for the branch + /// managed by [controller]. + /// + /// This ensures that we're subscribed to [_sourceStream] and that the + /// subscription isn't paused. + void _onListenOrResume(StreamController controller) { + if (controller.isClosed) return; + if (_subscription == null) { + _subscription = + _sourceStream.listen(_onData, onError: _onError, onDone: _onDone); + } else { + _subscription.resume(); + } + } + + /// The callback called when `onCancel` is called for the branch managed by + /// [controller]. + /// + /// This cancels or pauses the underlying subscription as necessary. If + /// [primary] is true, it also ensures that future calls to [fork] throw + /// [StateError]s. + Future _onCancel(StreamController controller, {bool primary: false}) { + if (primary) _isCanceled = true; + + if (controller.isClosed) return null; + _controllers.remove(controller); + + if (_controllers.isEmpty) return _subscription.cancel(); + + _onPause(controller); + return null; + } + + /// The callback called when `onPause` is called for the branch managed by + /// [controller]. + /// + /// This pauses the underlying subscription if necessary. + void _onPause(StreamController controller) { + if (controller.isClosed) return; + if (_subscription.isPaused) return; + if (_controllers.any((controller) => + controller.hasListener && !controller.isPaused)) { + return; + } + + _subscription.pause(); + } + + /// Forwards data events to all branches. + void _onData(value) { + // Don't iterate directly over the set because [controller.add] might cause + // it to be modified synchronously. + for (var controller in _controllers.toList()) { + controller.add(value); + } + } + + /// Forwards error events to all branches. + void _onError(error, StackTrace stackTrace) { + // Don't iterate directly over the set because [controller.addError] might + // cause it to be modified synchronously. + for (var controller in _controllers.toList()) { + controller.addError(error, stackTrace); + } + } + + /// Forwards close events to all branches. + void _onDone() { + _isCanceled = true; + + // Don't iterate directly over the set because [controller.close] might + // cause it to be modified synchronously. + for (var controller in _controllers.toList()) { + controller.close(); + } + _controllers.clear(); + } +} + diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index e4b3b640..b8941e15 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -7,6 +7,7 @@ library async.stream_events; import 'dart:async'; import 'dart:collection'; +import "forkable_stream.dart"; import "subscription_stream.dart"; import "stream_completer.dart"; import "../result.dart"; @@ -78,7 +79,7 @@ class StreamQueue { // by the content of the fifth event. /// Source of events. - final Stream _sourceStream; + final ForkableStream _sourceStream; /// Subscription on [_sourceStream] while listening for events. /// @@ -104,7 +105,9 @@ class StreamQueue { /// Create a `StreamQueue` of the events of [source]. StreamQueue(Stream source) - : _sourceStream = source; + : _sourceStream = source is ForkableStream + ? source + : new ForkableStream(source); /// Asks if the stream has any more events. /// @@ -216,6 +219,22 @@ class StreamQueue { throw _failClosed(); } + /// Creates a new stream queue in the same position as this one. + /// + /// The fork is subscribed to the same underlying stream as this queue, but + /// it's otherwise wholly independent. If requests are made on one, they don't + /// move the other forward; if one is closed, the other is still open. + /// + /// The underlying stream will only be paused when all forks have no + /// outstanding requests, and only canceled when all forks are canceled. + StreamQueue fork() { + if (_isClosed) throw _failClosed(); + + var request = new _ForkRequest(this); + _addRequest(request); + return request.queue; + } + /// Cancels the underlying stream subscription. /// /// If [immediate] is `false` (the default), the cancel operation waits until @@ -236,14 +255,15 @@ class StreamQueue { if (_isClosed) throw _failClosed(); _isClosed = true; + if (_isDone) return new Future.value(); + if (_subscription == null) _subscription = _sourceStream.listen(null); + if (!immediate) { var request = new _CancelRequest(this); _addRequest(request); return request.future; } - if (_isDone) return new Future.value(); - if (_subscription == null) _subscription = _sourceStream.listen(null); var future = _subscription.cancel(); _onDone(); return future; @@ -333,6 +353,7 @@ class StreamQueue { return; } } + if (!_isDone) { _subscription.pause(); } @@ -628,3 +649,50 @@ class _HasNextRequest implements _EventRequest { _completer.complete(false); } } + +/// Request for a [StreamQueue.fork] call. +class _ForkRequest implements _EventRequest { + /// Completer for the stream used by the queue by the `fork` call. + StreamCompleter _completer; + + StreamQueue queue; + + /// The [StreamQueue] object that has this request queued. + final StreamQueue _streamQueue; + + _ForkRequest(this._streamQueue) { + _completer = new StreamCompleter(); + queue = new StreamQueue(_completer.stream); + } + + bool addEvents(Queue events) { + _completeStream(events); + return true; + } + + void close(Queue events) { + _completeStream(events); + } + + void _completeStream(Queue events) { + if (events.isEmpty) { + if (_streamQueue._isDone) { + _completer.setEmpty(); + } else { + _completer.setSourceStream(_streamQueue._sourceStream.fork()); + } + } else { + // There are prefetched events which need to be added before the + // remaining stream. + var controller = new StreamController(); + for (var event in events) { + event.addTo(controller); + } + + var fork = _streamQueue._sourceStream.fork(); + controller.addStream(fork, cancelOnError: false) + .whenComplete(controller.close); + _completer.setSourceStream(controller.stream); + } + } +} diff --git a/pkgs/async/test/forkable_stream_test.dart b/pkgs/async/test/forkable_stream_test.dart new file mode 100644 index 00000000..80242a3a --- /dev/null +++ b/pkgs/async/test/forkable_stream_test.dart @@ -0,0 +1,413 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + var controller; + var stream; + setUp(() { + var cancelFuture = new Future.value(42); + controller = new StreamController(onCancel: () => cancelFuture); + stream = new ForkableStream(controller.stream); + }); + + group("with no forks", () { + test("forwards events, errors, and close", () async { + var queue = new StreamQueue(stream); + + controller.add(1); + expect(await queue.next, equals(1)); + + controller.add(2); + expect(await queue.next, equals(2)); + + controller.addError("error"); + expect(queue.next, throwsA("error")); + await flushMicrotasks(); + + controller.add(3); + expect(await queue.next, equals(3)); + + controller.close(); + expect(await queue.hasNext, isFalse); + }); + + test("listens to, pauses, and cancels the controller", () { + expect(controller.hasListener, isFalse); + + var sub = stream.listen(null); + expect(controller.hasListener, isTrue); + + sub.pause(); + expect(controller.isPaused, isTrue); + + sub.resume(); + expect(controller.isPaused, isFalse); + + sub.cancel(); + expect(controller.hasListener, isFalse); + }); + + test("unpauses the controller when a fork is listened", () { + stream.listen(null).pause(); + expect(controller.isPaused, isTrue); + + var fork = stream.fork(); + expect(controller.isPaused, isTrue); + + fork.listen(null); + expect(controller.isPaused, isFalse); + }); + }); + + group("with a fork created before the stream was listened", () { + var fork; + setUp(() { + fork = stream.fork(); + }); + + test("forwards events, errors, and close to both branches", () async { + var queue = new StreamQueue(stream); + var forkQueue = new StreamQueue(fork); + + controller.add(1); + expect(await queue.next, equals(1)); + expect(await forkQueue.next, equals(1)); + + controller.add(2); + expect(await queue.next, equals(2)); + expect(await forkQueue.next, equals(2)); + + controller.addError("error"); + expect(queue.next, throwsA("error")); + expect(forkQueue.next, throwsA("error")); + await flushMicrotasks(); + + controller.add(3); + expect(await queue.next, equals(3)); + expect(await forkQueue.next, equals(3)); + + controller.close(); + expect(await queue.hasNext, isFalse); + expect(await forkQueue.hasNext, isFalse); + }); + + test('listens to the source when the original is listened', () { + expect(controller.hasListener, isFalse); + stream.listen(null); + expect(controller.hasListener, isTrue); + }); + + test('listens to the source when the fork is listened', () { + expect(controller.hasListener, isFalse); + fork.listen(null); + expect(controller.hasListener, isTrue); + }); + }); + + test("with a fork created after the stream emitted a few events, forwards " + "future events, errors, and close to both branches", () async { + var queue = new StreamQueue(stream); + + controller.add(1); + expect(await queue.next, equals(1)); + + controller.add(2); + expect(await queue.next, equals(2)); + + var fork = stream.fork(); + var forkQueue = new StreamQueue(fork); + + controller.add(3); + expect(await queue.next, equals(3)); + expect(await forkQueue.next, equals(3)); + + controller.addError("error"); + expect(queue.next, throwsA("error")); + expect(forkQueue.next, throwsA("error")); + await flushMicrotasks(); + + controller.close(); + expect(await queue.hasNext, isFalse); + expect(await forkQueue.hasNext, isFalse); + }); + + group("with multiple forks", () { + var fork1; + var fork2; + var fork3; + var fork4; + setUp(() { + fork1 = stream.fork(); + fork2 = stream.fork(); + fork3 = stream.fork(); + fork4 = stream.fork(); + }); + + test("forwards events, errors, and close to all branches", () async { + var queue1 = new StreamQueue(stream); + var queue2 = new StreamQueue(fork1); + var queue3 = new StreamQueue(fork2); + var queue4 = new StreamQueue(fork3); + var queue5 = new StreamQueue(fork4); + + controller.add(1); + expect(await queue1.next, equals(1)); + expect(await queue2.next, equals(1)); + expect(await queue3.next, equals(1)); + expect(await queue4.next, equals(1)); + expect(await queue5.next, equals(1)); + + controller.add(2); + expect(await queue1.next, equals(2)); + expect(await queue2.next, equals(2)); + expect(await queue3.next, equals(2)); + expect(await queue4.next, equals(2)); + expect(await queue5.next, equals(2)); + + controller.addError("error"); + expect(queue1.next, throwsA("error")); + expect(queue2.next, throwsA("error")); + expect(queue3.next, throwsA("error")); + expect(queue4.next, throwsA("error")); + expect(queue5.next, throwsA("error")); + await flushMicrotasks(); + + controller.add(3); + expect(await queue1.next, equals(3)); + expect(await queue2.next, equals(3)); + expect(await queue3.next, equals(3)); + expect(await queue4.next, equals(3)); + expect(await queue5.next, equals(3)); + + controller.close(); + expect(await queue1.hasNext, isFalse); + expect(await queue2.hasNext, isFalse); + expect(await queue3.hasNext, isFalse); + expect(await queue4.hasNext, isFalse); + expect(await queue5.hasNext, isFalse); + }); + + test("forwards events in order of forking", () async { + var queue1 = new StreamQueue(stream); + var queue2 = new StreamQueue(fork1); + var queue3 = new StreamQueue(fork2); + var queue4 = new StreamQueue(fork3); + var queue5 = new StreamQueue(fork4); + + for (var i = 0; i < 4; i++) { + controller.add(i); + + var queue1Fired = false; + var queue2Fired = false; + var queue3Fired = false; + var queue4Fired = false; + var queue5Fired = false; + + queue5.next.then(expectAsync((_) { + queue5Fired = true; + expect(queue1Fired, isTrue); + expect(queue2Fired, isTrue); + expect(queue3Fired, isTrue); + expect(queue4Fired, isTrue); + })); + + queue1.next.then(expectAsync((_) { + queue1Fired = true; + expect(queue2Fired, isFalse); + expect(queue3Fired, isFalse); + expect(queue4Fired, isFalse); + expect(queue5Fired, isFalse); + })); + + queue4.next.then(expectAsync((_) { + queue4Fired = true; + expect(queue1Fired, isTrue); + expect(queue2Fired, isTrue); + expect(queue3Fired, isTrue); + expect(queue5Fired, isFalse); + })); + + queue2.next.then(expectAsync((_) { + queue2Fired = true; + expect(queue1Fired, isTrue); + expect(queue3Fired, isFalse); + expect(queue4Fired, isFalse); + expect(queue5Fired, isFalse); + })); + + queue3.next.then(expectAsync((_) { + queue3Fired = true; + expect(queue1Fired, isTrue); + expect(queue2Fired, isTrue); + expect(queue4Fired, isFalse); + expect(queue5Fired, isFalse); + })); + } + }); + + test("pauses the source when all forks are paused and/or not listening", + () { + var sub1 = stream.listen(null); + var sub2 = fork1.listen(null); + expect(controller.isPaused, isFalse); + + sub1.pause(); + expect(controller.isPaused, isFalse); + + sub2.pause(); + expect(controller.isPaused, isTrue); + + var sub3 = fork2.listen(null); + expect(controller.isPaused, isFalse); + + sub3.pause(); + expect(controller.isPaused, isTrue); + + sub2.resume(); + expect(controller.isPaused, isFalse); + + sub2.cancel(); + expect(controller.isPaused, isTrue); + }); + + test("cancels the source when all forks are canceled", () async { + var sub1 = stream.listen(null); + expect(controller.hasListener, isTrue); + + var sub2 = fork1.listen(null); + expect(controller.hasListener, isTrue); + + expect(sub1.cancel(), isNull); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + expect(sub2.cancel(), isNull); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + expect(fork2.listen(null).cancel(), isNull); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + expect(fork3.listen(null).cancel(), isNull); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + expect(fork4.listen(null).cancel(), completion(equals(42))); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + }); + }); + + group("modification during dispatch:", () { + test("forking during onCancel", () { + controller = new StreamController(onCancel: expectAsync(() { + expect(stream.fork().toList(), completion(isEmpty)); + })); + stream = new ForkableStream(controller.stream); + + stream.listen(null).cancel(); + }); + + test("forking during onPause", () { + controller = new StreamController(onPause: expectAsync(() { + stream.fork().listen(null); + })); + stream = new ForkableStream(controller.stream); + + stream.listen(null).pause(); + + // The fork created in onPause should have resumed the stream. + expect(controller.isPaused, isFalse); + }); + + test("forking during onData", () { + var sub; + sub = stream.listen(expectAsync((value1) { + expect(value1, equals(1)); + stream.fork().listen(expectAsync((value2) { + expect(value2, equals(2)); + })); + sub.cancel(); + })); + + controller.add(1); + controller.add(2); + }); + + test("canceling a fork during onData", () { + var fork = stream.fork(); + var forkSub = fork.listen(expectAsync((_) {}, count: 0)); + + stream.listen(expectAsync((_) => forkSub.cancel())); + controller.add(null); + }); + + test("forking during onError", () { + var sub; + sub = stream.listen(null, onError: expectAsync((error1) { + expect(error1, equals("error 1")); + stream.fork().listen(null, onError: expectAsync((error2) { + expect(error2, equals("error 2")); + })); + sub.cancel(); + })); + + controller.addError("error 1"); + controller.addError("error 2"); + }); + + test("canceling a fork during onError", () { + var fork = stream.fork(); + var forkSub = fork.listen(expectAsync((_) {}, count: 0)); + + stream.listen(null, onError: expectAsync((_) => forkSub.cancel())); + controller.addError("error"); + }); + + test("forking during onDone", () { + stream.listen(null, onDone: expectAsync(() { + expect(stream.fork().toList(), completion(isEmpty)); + })); + + controller.close(); + }); + + test("canceling a fork during onDone", () { + var fork = stream.fork(); + var forkSub = fork.listen(null, onDone: expectAsync(() {}, count: 0)); + + stream.listen(null, onDone: expectAsync(() => forkSub.cancel())); + controller.close(); + }); + }); + + group("throws an error when", () { + test("a cancelled stream is forked", () { + stream.listen(null).cancel(); + expect(stream.fork().toList(), completion(isEmpty)); + }); + + test("a cancelled stream is forked even when other forks are alive", () { + stream.fork().listen(null); + stream.listen(null).cancel(); + + expect(controller.hasListener, isTrue); + expect(stream.fork().toList(), completion(isEmpty)); + }); + + test("a closed stream is forked", () async { + controller.close(); + await stream.listen(null).asFuture(); + expect(stream.fork().toList(), completion(isEmpty)); + }); + }); +} diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 228ba8ad..3768bffc 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -629,6 +629,434 @@ main() { }); }); + group("fork operation", () { + test("produces a stream queue with the same events", () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + expect(await queue1.next, 1); + expect(await queue1.next, 2); + expect(await queue1.next, 3); + expect(await queue1.next, 4); + expect(await queue1.hasNext, isFalse); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("produces a stream queue with the same errors", () async { + var queue1 = new StreamQueue(createErrorStream()); + var queue2 = queue1.fork(); + + expect(await queue1.next, 1); + expect(await queue1.next, 2); + expect(queue1.next, throwsA("To err is divine!")); + expect(await queue1.next, 4); + expect(await queue1.hasNext, isFalse); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(queue2.next, throwsA("To err is divine!")); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("forks at the current point in the source queue", () { + var queue1 = new StreamQueue(createStream()); + + expect(queue1.next, completion(1)); + expect(queue1.next, completion(2)); + + var queue2 = queue1.fork(); + + expect(queue1.next, completion(3)); + expect(queue1.next, completion(4)); + expect(queue1.hasNext, completion(isFalse)); + + expect(queue2.next, completion(3)); + expect(queue2.next, completion(4)); + expect(queue2.hasNext, completion(isFalse)); + }); + + test("can be created after there are pending values", () async { + var queue1 = new StreamQueue(createStream()); + await flushMicrotasks(); + + var queue2 = queue1.fork(); + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("multiple forks can be created at different points", () async { + var queue1 = new StreamQueue(createStream()); + + var queue2 = queue1.fork(); + expect(await queue1.next, 1); + expect(await queue2.next, 1); + + var queue3 = queue1.fork(); + expect(await queue1.next, 2); + expect(await queue2.next, 2); + expect(await queue3.next, 2); + + var queue4 = queue1.fork(); + expect(await queue1.next, 3); + expect(await queue2.next, 3); + expect(await queue3.next, 3); + expect(await queue4.next, 3); + + var queue5 = queue1.fork(); + expect(await queue1.next, 4); + expect(await queue2.next, 4); + expect(await queue3.next, 4); + expect(await queue4.next, 4); + expect(await queue5.next, 4); + + var queue6 = queue1.fork(); + expect(await queue1.hasNext, isFalse); + expect(await queue2.hasNext, isFalse); + expect(await queue3.hasNext, isFalse); + expect(await queue4.hasNext, isFalse); + expect(await queue5.hasNext, isFalse); + expect(await queue6.hasNext, isFalse); + }); + + test("same-level forks receive data in the order they were created", + () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + var queue3 = queue1.fork(); + var queue4 = queue1.fork(); + var queue5 = queue1.fork(); + + for (var i = 0; i < 4; i++) { + var queue1Fired = false; + var queue2Fired = false; + var queue3Fired = false; + var queue4Fired = false; + var queue5Fired = false; + + queue5.next.then(expectAsync((_) { + queue5Fired = true; + expect(queue1Fired, isTrue); + expect(queue2Fired, isTrue); + expect(queue3Fired, isTrue); + expect(queue4Fired, isTrue); + })); + + queue1.next.then(expectAsync((_) { + queue1Fired = true; + expect(queue2Fired, isFalse); + expect(queue3Fired, isFalse); + expect(queue4Fired, isFalse); + expect(queue5Fired, isFalse); + })); + + queue4.next.then(expectAsync((_) { + queue4Fired = true; + expect(queue1Fired, isTrue); + expect(queue2Fired, isTrue); + expect(queue3Fired, isTrue); + expect(queue5Fired, isFalse); + })); + + queue2.next.then(expectAsync((_) { + queue2Fired = true; + expect(queue1Fired, isTrue); + expect(queue3Fired, isFalse); + expect(queue4Fired, isFalse); + expect(queue5Fired, isFalse); + })); + + queue3.next.then(expectAsync((_) { + queue3Fired = true; + expect(queue1Fired, isTrue); + expect(queue2Fired, isTrue); + expect(queue4Fired, isFalse); + expect(queue5Fired, isFalse); + })); + } + }); + + test("forks can be created from forks", () async { + var queue1 = new StreamQueue(createStream()); + + var queue2 = queue1.fork(); + expect(await queue1.next, 1); + expect(await queue2.next, 1); + + var queue3 = queue2.fork(); + expect(await queue1.next, 2); + expect(await queue2.next, 2); + expect(await queue3.next, 2); + + var queue4 = queue3.fork(); + expect(await queue1.next, 3); + expect(await queue2.next, 3); + expect(await queue3.next, 3); + expect(await queue4.next, 3); + + var queue5 = queue4.fork(); + expect(await queue1.next, 4); + expect(await queue2.next, 4); + expect(await queue3.next, 4); + expect(await queue4.next, 4); + expect(await queue5.next, 4); + + var queue6 = queue5.fork(); + expect(await queue1.hasNext, isFalse); + expect(await queue2.hasNext, isFalse); + expect(await queue3.hasNext, isFalse); + expect(await queue4.hasNext, isFalse); + expect(await queue5.hasNext, isFalse); + expect(await queue6.hasNext, isFalse); + }); + + group("canceling:", () { + test("cancelling a fork doesn't cancel its source", () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + queue2.cancel(); + expect(() => queue2.next, throwsStateError); + + expect(await queue1.next, 1); + expect(await queue1.next, 2); + expect(await queue1.next, 3); + expect(await queue1.next, 4); + expect(await queue1.hasNext, isFalse); + }); + + test("cancelling a source doesn't cancel its unmaterialized fork", + () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + queue1.cancel(); + expect(() => queue1.next, throwsStateError); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("cancelling a source doesn't cancel its materialized fork", + () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + expect(await queue1.next, 1); + + queue1.cancel(); + expect(() => queue1.next, throwsStateError); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("the underlying stream is only canceled once all forks are canceled", + () async { + var controller = new StreamController(); + var queue1 = new StreamQueue(controller.stream); + var queue2 = queue1.fork(); + + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + expect(queue1.next, completion(1)); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + queue2.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + controller.add(1); + queue1.cancel(); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + }); + + group("with immediate,", () { + test("cancelling a fork doesn't cancel its source", () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + queue2.cancel(immediate: true); + expect(() => queue2.next, throwsStateError); + + expect(await queue1.next, 1); + expect(await queue1.next, 2); + expect(await queue1.next, 3); + expect(await queue1.next, 4); + expect(await queue1.hasNext, isFalse); + }); + + test("cancelling a source doesn't cancel its unmaterialized fork", + () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + queue1.cancel(immediate: true); + expect(() => queue1.next, throwsStateError); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("cancelling a source doesn't cancel its materialized fork", + () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + expect(await queue1.next, 1); + + queue1.cancel(immediate: true); + expect(() => queue1.next, throwsStateError); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("the underlying stream is only canceled once all forks are " + "canceled", () async { + var controller = new StreamController(); + var queue1 = new StreamQueue(controller.stream); + var queue2 = queue1.fork(); + + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + expect(queue1.next, throwsStateError); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + queue2.cancel(immediate: true); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + + queue1.cancel(immediate: true); + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + }); + }); + }); + + group("pausing:", () { + test("the underlying stream is only implicitly paused when no forks are " + "awaiting input", () async { + var controller = new StreamController(); + var queue1 = new StreamQueue(controller.stream); + var queue2 = queue1.fork(); + + controller.add(1); + expect(await queue1.next, 1); + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isTrue); + + expect(queue1.next, completion(2)); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + controller.add(2); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + expect(queue2.next, completion(1)); + expect(queue2.next, completion(2)); + expect(queue2.next, completion(3)); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + controller.add(3); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + }); + + test("pausing a fork doesn't pause its source", () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + queue2.rest.listen(expectAsync((_) {}, count: 0)).pause(); + + expect(await queue1.next, 1); + expect(await queue1.next, 2); + expect(await queue1.next, 3); + expect(await queue1.next, 4); + expect(await queue1.hasNext, isFalse); + }); + + test("pausing a source doesn't pause its fork", () async { + var queue1 = new StreamQueue(createStream()); + var queue2 = queue1.fork(); + + queue1.rest.listen(expectAsync((_) {}, count: 0)).pause(); + + expect(await queue2.next, 1); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue2.next, 4); + expect(await queue2.hasNext, isFalse); + }); + + test("the underlying stream is only paused when all forks are paused", + () async { + var controller = new StreamController(); + var queue1 = new StreamQueue(controller.stream); + var queue2 = queue1.fork(); + + await flushMicrotasks(); + expect(controller.hasListener, isFalse); + + var sub1 = queue1.rest.listen(null); + await flushMicrotasks(); + expect(controller.hasListener, isTrue); + expect(controller.isPaused, isFalse); + + sub1.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + expect(queue2.next, completion(1)); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + controller.add(1); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + var sub2 = queue2.rest.listen(null); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + + sub2.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + sub1.resume(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + }); + }); + }); + test("all combinations sequential skip/next/take operations", () async { // Takes all combinations of two of next, skip and take, then ends with // doing rest. Each of the first rounds do 10 events of each type, From 24130e1d22e3b7ab1f5199c037730bef4019e0a4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 17 Jul 2015 12:50:00 -0700 Subject: [PATCH 041/260] Revert "Add StreamQueue.fork and ForkableStream." This reverts commit d3bd45c83be76cd12d7f9b682769c1a6cc135ed5. --- pkgs/async/CHANGELOG.md | 3 - pkgs/async/lib/async.dart | 1 - pkgs/async/lib/src/forkable_stream.dart | 166 --------- pkgs/async/lib/src/stream_queue.dart | 76 +--- pkgs/async/test/forkable_stream_test.dart | 413 --------------------- pkgs/async/test/stream_queue_test.dart | 428 ---------------------- 6 files changed, 4 insertions(+), 1083 deletions(-) delete mode 100644 pkgs/async/lib/src/forkable_stream.dart delete mode 100644 pkgs/async/test/forkable_stream_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d9d85d8e..39d204b2 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -10,9 +10,6 @@ - Added `SubscriptionStream` which creates a single-subscription stream from an existing stream subscription. -- Added `ForkableStream` which wraps a stream and allows independent forks to be - created that emit the same events as the original. - - Added a `ResultFuture` class for synchronously accessing the result of a wrapped future. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 6cfd6e14..71a18492 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -12,7 +12,6 @@ export "src/delegate/sink.dart"; export "src/delegate/stream_consumer.dart"; export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; -export "src/forkable_stream.dart"; export "src/future_group.dart"; export "src/result_future.dart"; export "src/stream_completer.dart"; diff --git a/pkgs/async/lib/src/forkable_stream.dart b/pkgs/async/lib/src/forkable_stream.dart deleted file mode 100644 index bb8f4652..00000000 --- a/pkgs/async/lib/src/forkable_stream.dart +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -library async.forkable_stream; - -import 'dart:async'; - -import 'stream_completer.dart'; - -/// A single-subscription stream from which other streams may be forked off at -/// the current position. -/// -/// This adds an operation, [fork], which produces a new stream that -/// independently emits the same events as this stream. Unlike the branches -/// produced by [StreamSplitter], a fork only emits events that arrive *after* -/// the call to [fork]. -/// -/// Each fork can be paused or canceled independently of one another and of this -/// stream. The underlying stream will be listened to once any branch is -/// listened to. It will be paused when all branches are paused or not yet -/// listened to. It will be canceled when all branches have been listened to and -/// then canceled. -class ForkableStream extends StreamView { - /// The underlying stream. - final Stream _sourceStream; - - /// The subscription to [_sourceStream]. - /// - /// This will be `null` until this stream or any of its forks are listened to. - StreamSubscription _subscription; - - /// Whether this has been cancelled and no more forks may be created. - bool _isCanceled = false; - - /// The controllers for any branches that have not yet been canceled. - /// - /// This includes a controller for this stream, until that has been cancelled. - final _controllers = new Set>(); - - /// Creates a new forkable stream wrapping [sourceStream]. - ForkableStream(Stream sourceStream) - // Use a completer here so that we can provide its stream to the - // superclass constructor while also adding the stream controller to - // [_controllers]. - : this._(sourceStream, new StreamCompleter()); - - ForkableStream._(this._sourceStream, StreamCompleter completer) - : super(completer.stream) { - completer.setSourceStream(_fork(primary: true)); - } - - /// Creates a new fork of this stream. - /// - /// From this point forward, the fork will emit the same events as this - /// stream. It will *not* emit any events that have already been emitted by - /// this stream. The fork is independent of this stream, which means each one - /// may be paused or canceled without affecting the other. - /// - /// If this stream is done or its subscription has been canceled, this returns - /// an empty stream. - Stream fork() => _fork(primary: false); - - /// Creates a stream forwarding [_sourceStream]. - /// - /// If [primary] is true, this is the stream underlying this object; - /// otherwise, it's a fork. The only difference is that when the primary - /// stream is canceled, [fork] starts throwing [StateError]s. - Stream _fork({bool primary: false}) { - if (_isCanceled) { - var controller = new StreamController()..close(); - return controller.stream; - } - - var controller; - controller = new StreamController( - onListen: () => _onListenOrResume(controller), - onCancel: () => _onCancel(controller, primary: primary), - onPause: () => _onPause(controller), - onResume: () => _onListenOrResume(controller), - sync: true); - - _controllers.add(controller); - - return controller.stream; - } - - /// The callback called when `onListen` or `onResume` is called for the branch - /// managed by [controller]. - /// - /// This ensures that we're subscribed to [_sourceStream] and that the - /// subscription isn't paused. - void _onListenOrResume(StreamController controller) { - if (controller.isClosed) return; - if (_subscription == null) { - _subscription = - _sourceStream.listen(_onData, onError: _onError, onDone: _onDone); - } else { - _subscription.resume(); - } - } - - /// The callback called when `onCancel` is called for the branch managed by - /// [controller]. - /// - /// This cancels or pauses the underlying subscription as necessary. If - /// [primary] is true, it also ensures that future calls to [fork] throw - /// [StateError]s. - Future _onCancel(StreamController controller, {bool primary: false}) { - if (primary) _isCanceled = true; - - if (controller.isClosed) return null; - _controllers.remove(controller); - - if (_controllers.isEmpty) return _subscription.cancel(); - - _onPause(controller); - return null; - } - - /// The callback called when `onPause` is called for the branch managed by - /// [controller]. - /// - /// This pauses the underlying subscription if necessary. - void _onPause(StreamController controller) { - if (controller.isClosed) return; - if (_subscription.isPaused) return; - if (_controllers.any((controller) => - controller.hasListener && !controller.isPaused)) { - return; - } - - _subscription.pause(); - } - - /// Forwards data events to all branches. - void _onData(value) { - // Don't iterate directly over the set because [controller.add] might cause - // it to be modified synchronously. - for (var controller in _controllers.toList()) { - controller.add(value); - } - } - - /// Forwards error events to all branches. - void _onError(error, StackTrace stackTrace) { - // Don't iterate directly over the set because [controller.addError] might - // cause it to be modified synchronously. - for (var controller in _controllers.toList()) { - controller.addError(error, stackTrace); - } - } - - /// Forwards close events to all branches. - void _onDone() { - _isCanceled = true; - - // Don't iterate directly over the set because [controller.close] might - // cause it to be modified synchronously. - for (var controller in _controllers.toList()) { - controller.close(); - } - _controllers.clear(); - } -} - diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index b8941e15..e4b3b640 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -7,7 +7,6 @@ library async.stream_events; import 'dart:async'; import 'dart:collection'; -import "forkable_stream.dart"; import "subscription_stream.dart"; import "stream_completer.dart"; import "../result.dart"; @@ -79,7 +78,7 @@ class StreamQueue { // by the content of the fifth event. /// Source of events. - final ForkableStream _sourceStream; + final Stream _sourceStream; /// Subscription on [_sourceStream] while listening for events. /// @@ -105,9 +104,7 @@ class StreamQueue { /// Create a `StreamQueue` of the events of [source]. StreamQueue(Stream source) - : _sourceStream = source is ForkableStream - ? source - : new ForkableStream(source); + : _sourceStream = source; /// Asks if the stream has any more events. /// @@ -219,22 +216,6 @@ class StreamQueue { throw _failClosed(); } - /// Creates a new stream queue in the same position as this one. - /// - /// The fork is subscribed to the same underlying stream as this queue, but - /// it's otherwise wholly independent. If requests are made on one, they don't - /// move the other forward; if one is closed, the other is still open. - /// - /// The underlying stream will only be paused when all forks have no - /// outstanding requests, and only canceled when all forks are canceled. - StreamQueue fork() { - if (_isClosed) throw _failClosed(); - - var request = new _ForkRequest(this); - _addRequest(request); - return request.queue; - } - /// Cancels the underlying stream subscription. /// /// If [immediate] is `false` (the default), the cancel operation waits until @@ -255,15 +236,14 @@ class StreamQueue { if (_isClosed) throw _failClosed(); _isClosed = true; - if (_isDone) return new Future.value(); - if (_subscription == null) _subscription = _sourceStream.listen(null); - if (!immediate) { var request = new _CancelRequest(this); _addRequest(request); return request.future; } + if (_isDone) return new Future.value(); + if (_subscription == null) _subscription = _sourceStream.listen(null); var future = _subscription.cancel(); _onDone(); return future; @@ -353,7 +333,6 @@ class StreamQueue { return; } } - if (!_isDone) { _subscription.pause(); } @@ -649,50 +628,3 @@ class _HasNextRequest implements _EventRequest { _completer.complete(false); } } - -/// Request for a [StreamQueue.fork] call. -class _ForkRequest implements _EventRequest { - /// Completer for the stream used by the queue by the `fork` call. - StreamCompleter _completer; - - StreamQueue queue; - - /// The [StreamQueue] object that has this request queued. - final StreamQueue _streamQueue; - - _ForkRequest(this._streamQueue) { - _completer = new StreamCompleter(); - queue = new StreamQueue(_completer.stream); - } - - bool addEvents(Queue events) { - _completeStream(events); - return true; - } - - void close(Queue events) { - _completeStream(events); - } - - void _completeStream(Queue events) { - if (events.isEmpty) { - if (_streamQueue._isDone) { - _completer.setEmpty(); - } else { - _completer.setSourceStream(_streamQueue._sourceStream.fork()); - } - } else { - // There are prefetched events which need to be added before the - // remaining stream. - var controller = new StreamController(); - for (var event in events) { - event.addTo(controller); - } - - var fork = _streamQueue._sourceStream.fork(); - controller.addStream(fork, cancelOnError: false) - .whenComplete(controller.close); - _completer.setSourceStream(controller.stream); - } - } -} diff --git a/pkgs/async/test/forkable_stream_test.dart b/pkgs/async/test/forkable_stream_test.dart deleted file mode 100644 index 80242a3a..00000000 --- a/pkgs/async/test/forkable_stream_test.dart +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright (c) 2015, 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. - -import 'dart:async'; - -import 'package:async/async.dart'; -import 'package:test/test.dart'; - -import 'utils.dart'; - -void main() { - var controller; - var stream; - setUp(() { - var cancelFuture = new Future.value(42); - controller = new StreamController(onCancel: () => cancelFuture); - stream = new ForkableStream(controller.stream); - }); - - group("with no forks", () { - test("forwards events, errors, and close", () async { - var queue = new StreamQueue(stream); - - controller.add(1); - expect(await queue.next, equals(1)); - - controller.add(2); - expect(await queue.next, equals(2)); - - controller.addError("error"); - expect(queue.next, throwsA("error")); - await flushMicrotasks(); - - controller.add(3); - expect(await queue.next, equals(3)); - - controller.close(); - expect(await queue.hasNext, isFalse); - }); - - test("listens to, pauses, and cancels the controller", () { - expect(controller.hasListener, isFalse); - - var sub = stream.listen(null); - expect(controller.hasListener, isTrue); - - sub.pause(); - expect(controller.isPaused, isTrue); - - sub.resume(); - expect(controller.isPaused, isFalse); - - sub.cancel(); - expect(controller.hasListener, isFalse); - }); - - test("unpauses the controller when a fork is listened", () { - stream.listen(null).pause(); - expect(controller.isPaused, isTrue); - - var fork = stream.fork(); - expect(controller.isPaused, isTrue); - - fork.listen(null); - expect(controller.isPaused, isFalse); - }); - }); - - group("with a fork created before the stream was listened", () { - var fork; - setUp(() { - fork = stream.fork(); - }); - - test("forwards events, errors, and close to both branches", () async { - var queue = new StreamQueue(stream); - var forkQueue = new StreamQueue(fork); - - controller.add(1); - expect(await queue.next, equals(1)); - expect(await forkQueue.next, equals(1)); - - controller.add(2); - expect(await queue.next, equals(2)); - expect(await forkQueue.next, equals(2)); - - controller.addError("error"); - expect(queue.next, throwsA("error")); - expect(forkQueue.next, throwsA("error")); - await flushMicrotasks(); - - controller.add(3); - expect(await queue.next, equals(3)); - expect(await forkQueue.next, equals(3)); - - controller.close(); - expect(await queue.hasNext, isFalse); - expect(await forkQueue.hasNext, isFalse); - }); - - test('listens to the source when the original is listened', () { - expect(controller.hasListener, isFalse); - stream.listen(null); - expect(controller.hasListener, isTrue); - }); - - test('listens to the source when the fork is listened', () { - expect(controller.hasListener, isFalse); - fork.listen(null); - expect(controller.hasListener, isTrue); - }); - }); - - test("with a fork created after the stream emitted a few events, forwards " - "future events, errors, and close to both branches", () async { - var queue = new StreamQueue(stream); - - controller.add(1); - expect(await queue.next, equals(1)); - - controller.add(2); - expect(await queue.next, equals(2)); - - var fork = stream.fork(); - var forkQueue = new StreamQueue(fork); - - controller.add(3); - expect(await queue.next, equals(3)); - expect(await forkQueue.next, equals(3)); - - controller.addError("error"); - expect(queue.next, throwsA("error")); - expect(forkQueue.next, throwsA("error")); - await flushMicrotasks(); - - controller.close(); - expect(await queue.hasNext, isFalse); - expect(await forkQueue.hasNext, isFalse); - }); - - group("with multiple forks", () { - var fork1; - var fork2; - var fork3; - var fork4; - setUp(() { - fork1 = stream.fork(); - fork2 = stream.fork(); - fork3 = stream.fork(); - fork4 = stream.fork(); - }); - - test("forwards events, errors, and close to all branches", () async { - var queue1 = new StreamQueue(stream); - var queue2 = new StreamQueue(fork1); - var queue3 = new StreamQueue(fork2); - var queue4 = new StreamQueue(fork3); - var queue5 = new StreamQueue(fork4); - - controller.add(1); - expect(await queue1.next, equals(1)); - expect(await queue2.next, equals(1)); - expect(await queue3.next, equals(1)); - expect(await queue4.next, equals(1)); - expect(await queue5.next, equals(1)); - - controller.add(2); - expect(await queue1.next, equals(2)); - expect(await queue2.next, equals(2)); - expect(await queue3.next, equals(2)); - expect(await queue4.next, equals(2)); - expect(await queue5.next, equals(2)); - - controller.addError("error"); - expect(queue1.next, throwsA("error")); - expect(queue2.next, throwsA("error")); - expect(queue3.next, throwsA("error")); - expect(queue4.next, throwsA("error")); - expect(queue5.next, throwsA("error")); - await flushMicrotasks(); - - controller.add(3); - expect(await queue1.next, equals(3)); - expect(await queue2.next, equals(3)); - expect(await queue3.next, equals(3)); - expect(await queue4.next, equals(3)); - expect(await queue5.next, equals(3)); - - controller.close(); - expect(await queue1.hasNext, isFalse); - expect(await queue2.hasNext, isFalse); - expect(await queue3.hasNext, isFalse); - expect(await queue4.hasNext, isFalse); - expect(await queue5.hasNext, isFalse); - }); - - test("forwards events in order of forking", () async { - var queue1 = new StreamQueue(stream); - var queue2 = new StreamQueue(fork1); - var queue3 = new StreamQueue(fork2); - var queue4 = new StreamQueue(fork3); - var queue5 = new StreamQueue(fork4); - - for (var i = 0; i < 4; i++) { - controller.add(i); - - var queue1Fired = false; - var queue2Fired = false; - var queue3Fired = false; - var queue4Fired = false; - var queue5Fired = false; - - queue5.next.then(expectAsync((_) { - queue5Fired = true; - expect(queue1Fired, isTrue); - expect(queue2Fired, isTrue); - expect(queue3Fired, isTrue); - expect(queue4Fired, isTrue); - })); - - queue1.next.then(expectAsync((_) { - queue1Fired = true; - expect(queue2Fired, isFalse); - expect(queue3Fired, isFalse); - expect(queue4Fired, isFalse); - expect(queue5Fired, isFalse); - })); - - queue4.next.then(expectAsync((_) { - queue4Fired = true; - expect(queue1Fired, isTrue); - expect(queue2Fired, isTrue); - expect(queue3Fired, isTrue); - expect(queue5Fired, isFalse); - })); - - queue2.next.then(expectAsync((_) { - queue2Fired = true; - expect(queue1Fired, isTrue); - expect(queue3Fired, isFalse); - expect(queue4Fired, isFalse); - expect(queue5Fired, isFalse); - })); - - queue3.next.then(expectAsync((_) { - queue3Fired = true; - expect(queue1Fired, isTrue); - expect(queue2Fired, isTrue); - expect(queue4Fired, isFalse); - expect(queue5Fired, isFalse); - })); - } - }); - - test("pauses the source when all forks are paused and/or not listening", - () { - var sub1 = stream.listen(null); - var sub2 = fork1.listen(null); - expect(controller.isPaused, isFalse); - - sub1.pause(); - expect(controller.isPaused, isFalse); - - sub2.pause(); - expect(controller.isPaused, isTrue); - - var sub3 = fork2.listen(null); - expect(controller.isPaused, isFalse); - - sub3.pause(); - expect(controller.isPaused, isTrue); - - sub2.resume(); - expect(controller.isPaused, isFalse); - - sub2.cancel(); - expect(controller.isPaused, isTrue); - }); - - test("cancels the source when all forks are canceled", () async { - var sub1 = stream.listen(null); - expect(controller.hasListener, isTrue); - - var sub2 = fork1.listen(null); - expect(controller.hasListener, isTrue); - - expect(sub1.cancel(), isNull); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - expect(sub2.cancel(), isNull); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - expect(fork2.listen(null).cancel(), isNull); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - expect(fork3.listen(null).cancel(), isNull); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - expect(fork4.listen(null).cancel(), completion(equals(42))); - await flushMicrotasks(); - expect(controller.hasListener, isFalse); - }); - }); - - group("modification during dispatch:", () { - test("forking during onCancel", () { - controller = new StreamController(onCancel: expectAsync(() { - expect(stream.fork().toList(), completion(isEmpty)); - })); - stream = new ForkableStream(controller.stream); - - stream.listen(null).cancel(); - }); - - test("forking during onPause", () { - controller = new StreamController(onPause: expectAsync(() { - stream.fork().listen(null); - })); - stream = new ForkableStream(controller.stream); - - stream.listen(null).pause(); - - // The fork created in onPause should have resumed the stream. - expect(controller.isPaused, isFalse); - }); - - test("forking during onData", () { - var sub; - sub = stream.listen(expectAsync((value1) { - expect(value1, equals(1)); - stream.fork().listen(expectAsync((value2) { - expect(value2, equals(2)); - })); - sub.cancel(); - })); - - controller.add(1); - controller.add(2); - }); - - test("canceling a fork during onData", () { - var fork = stream.fork(); - var forkSub = fork.listen(expectAsync((_) {}, count: 0)); - - stream.listen(expectAsync((_) => forkSub.cancel())); - controller.add(null); - }); - - test("forking during onError", () { - var sub; - sub = stream.listen(null, onError: expectAsync((error1) { - expect(error1, equals("error 1")); - stream.fork().listen(null, onError: expectAsync((error2) { - expect(error2, equals("error 2")); - })); - sub.cancel(); - })); - - controller.addError("error 1"); - controller.addError("error 2"); - }); - - test("canceling a fork during onError", () { - var fork = stream.fork(); - var forkSub = fork.listen(expectAsync((_) {}, count: 0)); - - stream.listen(null, onError: expectAsync((_) => forkSub.cancel())); - controller.addError("error"); - }); - - test("forking during onDone", () { - stream.listen(null, onDone: expectAsync(() { - expect(stream.fork().toList(), completion(isEmpty)); - })); - - controller.close(); - }); - - test("canceling a fork during onDone", () { - var fork = stream.fork(); - var forkSub = fork.listen(null, onDone: expectAsync(() {}, count: 0)); - - stream.listen(null, onDone: expectAsync(() => forkSub.cancel())); - controller.close(); - }); - }); - - group("throws an error when", () { - test("a cancelled stream is forked", () { - stream.listen(null).cancel(); - expect(stream.fork().toList(), completion(isEmpty)); - }); - - test("a cancelled stream is forked even when other forks are alive", () { - stream.fork().listen(null); - stream.listen(null).cancel(); - - expect(controller.hasListener, isTrue); - expect(stream.fork().toList(), completion(isEmpty)); - }); - - test("a closed stream is forked", () async { - controller.close(); - await stream.listen(null).asFuture(); - expect(stream.fork().toList(), completion(isEmpty)); - }); - }); -} diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 3768bffc..228ba8ad 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -629,434 +629,6 @@ main() { }); }); - group("fork operation", () { - test("produces a stream queue with the same events", () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - expect(await queue1.next, 1); - expect(await queue1.next, 2); - expect(await queue1.next, 3); - expect(await queue1.next, 4); - expect(await queue1.hasNext, isFalse); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("produces a stream queue with the same errors", () async { - var queue1 = new StreamQueue(createErrorStream()); - var queue2 = queue1.fork(); - - expect(await queue1.next, 1); - expect(await queue1.next, 2); - expect(queue1.next, throwsA("To err is divine!")); - expect(await queue1.next, 4); - expect(await queue1.hasNext, isFalse); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(queue2.next, throwsA("To err is divine!")); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("forks at the current point in the source queue", () { - var queue1 = new StreamQueue(createStream()); - - expect(queue1.next, completion(1)); - expect(queue1.next, completion(2)); - - var queue2 = queue1.fork(); - - expect(queue1.next, completion(3)); - expect(queue1.next, completion(4)); - expect(queue1.hasNext, completion(isFalse)); - - expect(queue2.next, completion(3)); - expect(queue2.next, completion(4)); - expect(queue2.hasNext, completion(isFalse)); - }); - - test("can be created after there are pending values", () async { - var queue1 = new StreamQueue(createStream()); - await flushMicrotasks(); - - var queue2 = queue1.fork(); - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("multiple forks can be created at different points", () async { - var queue1 = new StreamQueue(createStream()); - - var queue2 = queue1.fork(); - expect(await queue1.next, 1); - expect(await queue2.next, 1); - - var queue3 = queue1.fork(); - expect(await queue1.next, 2); - expect(await queue2.next, 2); - expect(await queue3.next, 2); - - var queue4 = queue1.fork(); - expect(await queue1.next, 3); - expect(await queue2.next, 3); - expect(await queue3.next, 3); - expect(await queue4.next, 3); - - var queue5 = queue1.fork(); - expect(await queue1.next, 4); - expect(await queue2.next, 4); - expect(await queue3.next, 4); - expect(await queue4.next, 4); - expect(await queue5.next, 4); - - var queue6 = queue1.fork(); - expect(await queue1.hasNext, isFalse); - expect(await queue2.hasNext, isFalse); - expect(await queue3.hasNext, isFalse); - expect(await queue4.hasNext, isFalse); - expect(await queue5.hasNext, isFalse); - expect(await queue6.hasNext, isFalse); - }); - - test("same-level forks receive data in the order they were created", - () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - var queue3 = queue1.fork(); - var queue4 = queue1.fork(); - var queue5 = queue1.fork(); - - for (var i = 0; i < 4; i++) { - var queue1Fired = false; - var queue2Fired = false; - var queue3Fired = false; - var queue4Fired = false; - var queue5Fired = false; - - queue5.next.then(expectAsync((_) { - queue5Fired = true; - expect(queue1Fired, isTrue); - expect(queue2Fired, isTrue); - expect(queue3Fired, isTrue); - expect(queue4Fired, isTrue); - })); - - queue1.next.then(expectAsync((_) { - queue1Fired = true; - expect(queue2Fired, isFalse); - expect(queue3Fired, isFalse); - expect(queue4Fired, isFalse); - expect(queue5Fired, isFalse); - })); - - queue4.next.then(expectAsync((_) { - queue4Fired = true; - expect(queue1Fired, isTrue); - expect(queue2Fired, isTrue); - expect(queue3Fired, isTrue); - expect(queue5Fired, isFalse); - })); - - queue2.next.then(expectAsync((_) { - queue2Fired = true; - expect(queue1Fired, isTrue); - expect(queue3Fired, isFalse); - expect(queue4Fired, isFalse); - expect(queue5Fired, isFalse); - })); - - queue3.next.then(expectAsync((_) { - queue3Fired = true; - expect(queue1Fired, isTrue); - expect(queue2Fired, isTrue); - expect(queue4Fired, isFalse); - expect(queue5Fired, isFalse); - })); - } - }); - - test("forks can be created from forks", () async { - var queue1 = new StreamQueue(createStream()); - - var queue2 = queue1.fork(); - expect(await queue1.next, 1); - expect(await queue2.next, 1); - - var queue3 = queue2.fork(); - expect(await queue1.next, 2); - expect(await queue2.next, 2); - expect(await queue3.next, 2); - - var queue4 = queue3.fork(); - expect(await queue1.next, 3); - expect(await queue2.next, 3); - expect(await queue3.next, 3); - expect(await queue4.next, 3); - - var queue5 = queue4.fork(); - expect(await queue1.next, 4); - expect(await queue2.next, 4); - expect(await queue3.next, 4); - expect(await queue4.next, 4); - expect(await queue5.next, 4); - - var queue6 = queue5.fork(); - expect(await queue1.hasNext, isFalse); - expect(await queue2.hasNext, isFalse); - expect(await queue3.hasNext, isFalse); - expect(await queue4.hasNext, isFalse); - expect(await queue5.hasNext, isFalse); - expect(await queue6.hasNext, isFalse); - }); - - group("canceling:", () { - test("cancelling a fork doesn't cancel its source", () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - queue2.cancel(); - expect(() => queue2.next, throwsStateError); - - expect(await queue1.next, 1); - expect(await queue1.next, 2); - expect(await queue1.next, 3); - expect(await queue1.next, 4); - expect(await queue1.hasNext, isFalse); - }); - - test("cancelling a source doesn't cancel its unmaterialized fork", - () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - queue1.cancel(); - expect(() => queue1.next, throwsStateError); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("cancelling a source doesn't cancel its materialized fork", - () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - expect(await queue1.next, 1); - - queue1.cancel(); - expect(() => queue1.next, throwsStateError); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("the underlying stream is only canceled once all forks are canceled", - () async { - var controller = new StreamController(); - var queue1 = new StreamQueue(controller.stream); - var queue2 = queue1.fork(); - - await flushMicrotasks(); - expect(controller.hasListener, isFalse); - - expect(queue1.next, completion(1)); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - queue2.cancel(); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - controller.add(1); - queue1.cancel(); - await flushMicrotasks(); - expect(controller.hasListener, isFalse); - }); - - group("with immediate,", () { - test("cancelling a fork doesn't cancel its source", () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - queue2.cancel(immediate: true); - expect(() => queue2.next, throwsStateError); - - expect(await queue1.next, 1); - expect(await queue1.next, 2); - expect(await queue1.next, 3); - expect(await queue1.next, 4); - expect(await queue1.hasNext, isFalse); - }); - - test("cancelling a source doesn't cancel its unmaterialized fork", - () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - queue1.cancel(immediate: true); - expect(() => queue1.next, throwsStateError); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("cancelling a source doesn't cancel its materialized fork", - () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - expect(await queue1.next, 1); - - queue1.cancel(immediate: true); - expect(() => queue1.next, throwsStateError); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("the underlying stream is only canceled once all forks are " - "canceled", () async { - var controller = new StreamController(); - var queue1 = new StreamQueue(controller.stream); - var queue2 = queue1.fork(); - - await flushMicrotasks(); - expect(controller.hasListener, isFalse); - - expect(queue1.next, throwsStateError); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - queue2.cancel(immediate: true); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - - queue1.cancel(immediate: true); - await flushMicrotasks(); - expect(controller.hasListener, isFalse); - }); - }); - }); - - group("pausing:", () { - test("the underlying stream is only implicitly paused when no forks are " - "awaiting input", () async { - var controller = new StreamController(); - var queue1 = new StreamQueue(controller.stream); - var queue2 = queue1.fork(); - - controller.add(1); - expect(await queue1.next, 1); - expect(controller.hasListener, isTrue); - expect(controller.isPaused, isTrue); - - expect(queue1.next, completion(2)); - await flushMicrotasks(); - expect(controller.isPaused, isFalse); - - controller.add(2); - await flushMicrotasks(); - expect(controller.isPaused, isTrue); - - expect(queue2.next, completion(1)); - expect(queue2.next, completion(2)); - expect(queue2.next, completion(3)); - await flushMicrotasks(); - expect(controller.isPaused, isFalse); - - controller.add(3); - await flushMicrotasks(); - expect(controller.isPaused, isTrue); - }); - - test("pausing a fork doesn't pause its source", () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - queue2.rest.listen(expectAsync((_) {}, count: 0)).pause(); - - expect(await queue1.next, 1); - expect(await queue1.next, 2); - expect(await queue1.next, 3); - expect(await queue1.next, 4); - expect(await queue1.hasNext, isFalse); - }); - - test("pausing a source doesn't pause its fork", () async { - var queue1 = new StreamQueue(createStream()); - var queue2 = queue1.fork(); - - queue1.rest.listen(expectAsync((_) {}, count: 0)).pause(); - - expect(await queue2.next, 1); - expect(await queue2.next, 2); - expect(await queue2.next, 3); - expect(await queue2.next, 4); - expect(await queue2.hasNext, isFalse); - }); - - test("the underlying stream is only paused when all forks are paused", - () async { - var controller = new StreamController(); - var queue1 = new StreamQueue(controller.stream); - var queue2 = queue1.fork(); - - await flushMicrotasks(); - expect(controller.hasListener, isFalse); - - var sub1 = queue1.rest.listen(null); - await flushMicrotasks(); - expect(controller.hasListener, isTrue); - expect(controller.isPaused, isFalse); - - sub1.pause(); - await flushMicrotasks(); - expect(controller.isPaused, isTrue); - - expect(queue2.next, completion(1)); - await flushMicrotasks(); - expect(controller.isPaused, isFalse); - - controller.add(1); - await flushMicrotasks(); - expect(controller.isPaused, isTrue); - - var sub2 = queue2.rest.listen(null); - await flushMicrotasks(); - expect(controller.isPaused, isFalse); - - sub2.pause(); - await flushMicrotasks(); - expect(controller.isPaused, isTrue); - - sub1.resume(); - await flushMicrotasks(); - expect(controller.isPaused, isFalse); - }); - }); - }); - test("all combinations sequential skip/next/take operations", () async { // Takes all combinations of two of next, skip and take, then ends with // doing rest. Each of the first rounds do 10 events of each type, From 8ec7cc57bafdaf39a43f680b537fc1fbcab5ef91 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 17 Jul 2015 13:41:24 -0700 Subject: [PATCH 042/260] Use the new test runner on the bots. R=lrn@google.com Review URL: https://codereview.chromium.org//1237893005 . --- pkgs/async/.status | 12 ------------ pkgs/async/.test_config | 3 +++ 2 files changed, 3 insertions(+), 12 deletions(-) delete mode 100644 pkgs/async/.status create mode 100644 pkgs/async/.test_config diff --git a/pkgs/async/.status b/pkgs/async/.status deleted file mode 100644 index ebc5f813..00000000 --- a/pkgs/async/.status +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2015, 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. - -# Skip non-test files ending with "_test". -packages/*: Skip -*/packages/*: Skip -*/*/packages/*: Skip -*/*/*/packages/*: Skip -*/*/*/*packages/*: Skip -*/*/*/*/*packages/*: Skip - diff --git a/pkgs/async/.test_config b/pkgs/async/.test_config new file mode 100644 index 00000000..412fc5c5 --- /dev/null +++ b/pkgs/async/.test_config @@ -0,0 +1,3 @@ +{ + "test_package": true +} \ No newline at end of file From 132479f24d48c942294a30654e7219abc2f29c67 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 21 Jul 2015 11:09:31 -0700 Subject: [PATCH 043/260] Reword the documentation for StreamQueue.cancel. R=floitsch@google.com Review URL: https://codereview.chromium.org//1243783003 . --- pkgs/async/lib/src/stream_queue.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index e4b3b640..36d03ef1 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -223,8 +223,8 @@ class StreamQueue { /// subscription providing the events. /// /// If [immediate] is `true`, the subscription is instead canceled - /// immediately. Any pending events are completed as though the underlying - /// stream had closed. + /// immediately. Any pending events complete with a 'closed'-event, as though + /// the stream had closed by itself. /// /// The returned future completes with the result of calling /// `cancel`. From 14f3fdf52a75efb9900d2084ef0d738623ac7860 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 23 Jul 2015 11:09:56 -0700 Subject: [PATCH 044/260] Don't use "=>" for void methods in delegates. "=>" breaks if the delegate wraps a subclass that adds a return type (e.g. DelegatingSink wrapping a StreamSink). R=rnystrom@google.com Review URL: https://codereview.chromium.org//1242943010 . --- pkgs/async/lib/src/delegate/event_sink.dart | 13 +++++++++---- pkgs/async/lib/src/delegate/sink.dart | 8 ++++++-- pkgs/async/lib/src/delegate/stream_sink.dart | 9 ++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 1fe86c15..337e7a81 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -16,10 +16,15 @@ class DelegatingEventSink implements EventSink { /// Create a delegating sink forwarding calls to [sink]. DelegatingEventSink(EventSink sink) : _sink = sink; - void add(T data) => _sink.add(data); + void add(T data) { + _sink.add(data); + } - void addError(error, [StackTrace stackTrace]) => - _sink.addError(error, stackTrace); + void addError(error, [StackTrace stackTrace]) { + _sink.addError(error, stackTrace); + } - void close() => _sink.close(); + void close() { + _sink.close(); + } } diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index 5cf4f563..bb50da33 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -15,7 +15,11 @@ class DelegatingSink implements Sink { DelegatingSink(Sink sink) : _sink = sink; - void add(T data) => _sink.add(data); + void add(T data) { + _sink.add(data); + } - void close() => _sink.close(); + void close() { + _sink.close(); + } } diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index 9742b4ce..b6ace650 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -19,10 +19,13 @@ class DelegatingStreamSink implements StreamSink { DelegatingStreamSink(StreamSink sink) : _sink = sink; - void add(T data) => _sink.add(data); + void add(T data) { + _sink.add(data); + } - void addError(error, [StackTrace stackTrace]) => - _sink.addError(error, stackTrace); + void addError(error, [StackTrace stackTrace]) { + _sink.addError(error, stackTrace); + } Future addStream(Stream stream) => _sink.addStream(stream); From 83a06edef028e6813b3825d243962a999d54377a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 10 Aug 2015 15:57:55 -0700 Subject: [PATCH 045/260] Release 1.3.0. There are still some changes in-flight, but we need this release for test 0.12.4. R=kevmoo@google.com Review URL: https://codereview.chromium.org//1284723003 . --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 74f5ff95..84b8de5b 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.3.0-dev +version: 1.3.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 18dd0dfe9cd381a14b03ba9adae70ab9975bf236 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 25 Aug 2015 09:51:50 +0200 Subject: [PATCH 046/260] Make it possible to not complete a request when the event source is done. Abstract the event source of a stream queue so it doesn't have to be a stream. This is in anticipation of adding look-ahead which will need to keep the request alive until the user fast-forwards or rewindes, even after the original event source is done, and which will need a stream queue fed by the look-ahead request, not by a stream. R=nweiz@google.com Review URL: https://codereview.chromium.org//1305063002 . --- pkgs/async/lib/src/stream_queue.dart | 360 ++++++++++++++------------- 1 file changed, 189 insertions(+), 171 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 36d03ef1..09b3a75b 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -60,15 +60,17 @@ import "../result.dart"; /// /// When you need no further events the `StreamQueue` should be closed /// using [cancel]. This releases the underlying stream subscription. -class StreamQueue { +abstract class StreamQueue { // This class maintains two queues: one of events and one of requests. // The active request (the one in front of the queue) is called with - // the current event queue when it becomes active. + // the current event queue when it becomes active, every time a + // new event arrives, and when the event source closes. // - // If the request returns true, it's complete and will be removed from the + // If the request returns `true`, it's complete and will be removed from the // request queue. - // If the request returns false, it needs more events, and will be called - // again when new events are available. + // If the request returns `false`, it needs more events, and will be called + // again when new events are available. It may trigger a call itself by + // calling [_updateRequests]. // The request can remove events that it uses, or keep them in the event // queue until it has all that it needs. // @@ -77,16 +79,7 @@ class StreamQueue { // potentially a request that takes either five or zero events, determined // by the content of the fifth event. - /// Source of events. - final Stream _sourceStream; - - /// Subscription on [_sourceStream] while listening for events. - /// - /// Set to subscription when listening, and set to `null` when the - /// subscription is done (and [_isDone] is set to true). - StreamSubscription _subscription; - - /// Whether we have listened on [_sourceStream] and the subscription is done. + /// Whether the event source is done. bool _isDone = false; /// Whether a closing operation has been performed on the stream queue. @@ -103,8 +96,9 @@ class StreamQueue { final Queue<_EventRequest> _requestQueue = new Queue(); /// Create a `StreamQueue` of the events of [source]. - StreamQueue(Stream source) - : _sourceStream = source; + factory StreamQueue(Stream source) = _StreamQueue; + + StreamQueue._(); /// Asks if the stream has any more events. /// @@ -115,6 +109,8 @@ class StreamQueue { /// /// Can be used before using [next] to avoid getting an error in the /// future returned by `next` in the case where there are no more events. + /// Another alternative is to use `take(1)` which returns either zero or + /// one events. Future get hasNext { if (!_isClosed) { var hasNextRequest = new _HasNextRequest(); @@ -216,15 +212,15 @@ class StreamQueue { throw _failClosed(); } - /// Cancels the underlying stream subscription. + /// Cancels the underlying event source. /// /// If [immediate] is `false` (the default), the cancel operation waits until /// all previously requested events have been processed, then it cancels the /// subscription providing the events. /// - /// If [immediate] is `true`, the subscription is instead canceled - /// immediately. Any pending events complete with a 'closed'-event, as though - /// the stream had closed by itself. + /// If [immediate] is `true`, the source is instead canceled + /// immediately. Any pending events are completed as though the underlying + /// stream had closed. /// /// The returned future completes with the result of calling /// `cancel`. @@ -242,114 +238,178 @@ class StreamQueue { return request.future; } - if (_isDone) return new Future.value(); - if (_subscription == null) _subscription = _sourceStream.listen(null); - var future = _subscription.cancel(); - _onDone(); - return future; + if (_isDone && _eventQueue.isEmpty) return new Future.value(); + return _cancel(); } - /// Returns an error for when a request is made after cancel. + // ------------------------------------------------------------------ + // Methods that may be called from the request implementations to + // control the even stream. + + /// Matches events with requests. /// - /// Returns a [StateError] with a message saying that either - /// [cancel] or [rest] have already been called. - Error _failClosed() { - return new StateError("Already cancelled"); + /// Called after receiving an event or when the event source closes. + /// + /// May be called by requests which have returned `false` (saying they + /// are not yet done) so they can be checked again before any new + /// events arrive. + /// Any request returing `false` from `update` when `isDone` is `true` + /// *must* call `_updateRequests` when they are ready to continue + /// (since no further events will trigger the call). + void _updateRequests() { + while (_requestQueue.isNotEmpty) { + if (_requestQueue.first.update(_eventQueue, _isDone)) { + _requestQueue.removeFirst(); + } else { + return; + } + } + + if (!_isDone) { + _pause(); + } } - // Callbacks receiving the events of the source stream. + /// Extracts a stream from the event source and makes this stream queue + /// unusable. + /// + /// Can only be used by the very last request (the stream queue must + /// be closed by that request). + /// Only used by [rest]. + Stream _extractStream(); - void _onData(T data) { - _eventQueue.add(new Result.value(data)); - _checkQueues(); - } + /// Requests that the event source pauses events. + /// + /// This is called automatically when the request queue is empty. + /// + /// The event source is restarted by the next call to [_ensureListening]. + void _pause(); - void _onError(error, StackTrace stack) { - _eventQueue.add(new Result.error(error, stack)); - _checkQueues(); + /// Ensures that we are listening on events from the event source. + /// + /// Starts listening for the first time or resumes after a [_pause]. + /// + /// Is called automatically if a request requires more events. + void _ensureListening(); + + /// Cancels the underlying event source. + Future _cancel(); + + // ------------------------------------------------------------------ + // Methods called by the event source to add events or say that it's + // done. + + /// Called when the event source adds a new data or error event. + /// Always calls [_updateRequests] after adding. + void _addResult(Result result) { + _eventQueue.add(result); + _updateRequests(); } - void _onDone() { - _subscription = null; + /// Called when the event source is done. + /// Always calls [_updateRequests] after adding. + void _close() { _isDone = true; - _closeAllRequests(); + _updateRequests(); } - // Request queue management. + // ------------------------------------------------------------------ + // Internal helper methods. + + /// Returns an error for when a request is made after cancel. + /// + /// Returns a [StateError] with a message saying that either + /// [cancel] or [rest] have already been called. + Error _failClosed() { + return new StateError("Already cancelled"); + } /// Adds a new request to the queue. + /// + /// If the request queue is empty and the request can be completed + /// immediately, it skips the queue. void _addRequest(_EventRequest request) { - if (_isDone) { - assert(_requestQueue.isEmpty); - if (!request.addEvents(_eventQueue)) { - request.close(_eventQueue); - } - return; - } if (_requestQueue.isEmpty) { - if (request.addEvents(_eventQueue)) return; + if (request.update(_eventQueue, _isDone)) return; _ensureListening(); } _requestQueue.add(request); } +} + - /// Ensures that we are listening on events from [_sourceStream]. +/// The default implementation of [StreamQueue]. +/// +/// This queue gets its events from a stream which is listened +/// to when a request needs events. +class _StreamQueue extends StreamQueue { + /// Source of events. + final Stream _sourceStream; + + /// Subscription on [_sourceStream] while listening for events. /// - /// Resumes subscription on [_sourceStream], or creates it if necessary. + /// Set to subscription when listening, and set to `null` when the + /// subscription is done (and [_isDone] is set to true). + StreamSubscription _subscription; + + _StreamQueue(this._sourceStream) : super._(); + + Future _cancel() { + if (_isDone) return null; + if (_subscription == null) _subscription = _sourceStream.listen(null); + var future = _subscription.cancel(); + _close(); + return future; + } + void _ensureListening() { assert(!_isDone); if (_subscription == null) { _subscription = - _sourceStream.listen(_onData, onError: _onError, onDone: _onDone); + _sourceStream.listen( + (data) { + _addResult(new Result.value(data)); + }, + onError: (error, StackTrace stackTrace) { + _addResult(new Result.error(error, stackTrace)); + }, + onDone: () { + _subscription = null; + this._close(); + }); } else { _subscription.resume(); } } - /// Removes all requests and closes them. - /// - /// Used when the source stream is done. - /// After this, no further requests will be added to the queue, - /// requests are immediately served entirely by events already in the event - /// queue, if any. - void _closeAllRequests() { - assert(_isDone); - while (_requestQueue.isNotEmpty) { - var request = _requestQueue.removeFirst(); - if (!request.addEvents(_eventQueue)) { - request.close(_eventQueue); - } - } + void _pause() { + _subscription.pause(); } - /// Matches events with requests. - /// - /// Called after receiving an event. - void _checkQueues() { - while (_requestQueue.isNotEmpty) { - if (_requestQueue.first.addEvents(_eventQueue)) { - _requestQueue.removeFirst(); - } else { - return; - } + Stream _extractStream() { + assert(_isClosed); + if (_isDone) { + return new Stream.empty(); } - if (!_isDone) { - _subscription.pause(); + + if (_subscription == null) { + return _sourceStream; } - } - /// Extracts the subscription and makes this stream queue unusable. - /// - /// Can only be used by the very last request. - StreamSubscription _dispose() { - assert(_isClosed); var subscription = _subscription; _subscription = null; _isDone = true; - return subscription; + + var wasPaused = subscription.isPaused; + var result = new SubscriptionStream(subscription); + // Resume after creating stream because that pauses the subscription too. + // This way there won't be a short resumption in the middle. + if (wasPaused) subscription.resume(); + return result; } } + /// Request object that receives events when they arrive, until fulfilled. /// /// Each request that cannot be fulfilled immediately is represented by @@ -367,7 +427,7 @@ class StreamQueue { abstract class _EventRequest { /// Handle available events. /// - /// The available events are provided as a queue. The `addEvents` function + /// The available events are provided as a queue. The `update` function /// should only remove events from the front of the event queue, e.g., /// using [removeFirst]. /// @@ -382,22 +442,10 @@ abstract class _EventRequest { /// This method is called when a request reaches the front of the request /// queue, and if it returns `false`, it's called again every time a new event /// becomes available, or when the stream closes. - bool addEvents(Queue events); - - /// Complete the request. - /// - /// This is called when the source stream is done before the request - /// had a chance to receive all its events. That is, after a call - /// to [addEvents] has returned `false`. - /// If there are any unused events available, they are in the [events] queue. - /// No further events will become available. - /// - /// The queue should only remove events from the front of the event queue, - /// e.g., using [removeFirst]. - /// - /// If the request kept events in the queue after an [addEvents] call, - /// this is the last chance to use them. - void close(Queue events); + /// If the function returns `false` when the stream has already closed + /// ([isDone] is true), then the request must call + /// [StreamQueue._updateRequests] itself when it's ready to continue. + bool update(Queue events, bool isDone); } /// Request for a [StreamQueue.next] call. @@ -412,16 +460,18 @@ class _NextRequest implements _EventRequest { Future get future => _completer.future; - bool addEvents(Queue events) { - if (events.isEmpty) return false; - events.removeFirst().complete(_completer); - return true; - } - - void close(Queue events) { - var errorFuture = - new Future.sync(() => throw new StateError("No elements")); - _completer.complete(errorFuture); + bool update(Queue events, bool isDone) { + if (events.isNotEmpty) { + events.removeFirst().complete(_completer); + return true; + } + if (isDone) { + var errorFuture = + new Future.sync(() => throw new StateError("No elements")); + _completer.complete(errorFuture); + return true; + } + return false; } } @@ -443,22 +493,22 @@ class _SkipRequest implements _EventRequest { /// The future completed when the correct number of events have been skipped. Future get future => _completer.future; - bool addEvents(Queue events) { + bool update(Queue events, bool isDone) { while (_eventsToSkip > 0) { - if (events.isEmpty) return false; + if (events.isEmpty) { + if (isDone) break; + return false; + } _eventsToSkip--; + var event = events.removeFirst(); if (event.isError) { event.complete(_completer); return true; } } - _completer.complete(0); - return true; - } - - void close(Queue events) { _completer.complete(_eventsToSkip); + return true; } } @@ -481,9 +531,13 @@ class _TakeRequest implements _EventRequest { /// The future completed when the correct number of events have been captured. Future get future => _completer.future; - bool addEvents(Queue events) { + bool update(Queue events, bool isDone) { while (_list.length < _eventsToTake) { - if (events.isEmpty) return false; + if (events.isEmpty) { + if (isDone) break; + return false; + } + var result = events.removeFirst(); if (result.isError) { result.complete(_completer); @@ -494,10 +548,6 @@ class _TakeRequest implements _EventRequest { _completer.complete(_list); return true; } - - void close(Queue events) { - _completer.complete(_list); - } } /// Request for a [StreamQueue.cancel] call. @@ -520,22 +570,14 @@ class _CancelRequest implements _EventRequest { /// The future completed when the cancel request is completed. Future get future => _completer.future; - bool addEvents(Queue events) { - _shutdown(); - return true; - } - - void close(_) { - _shutdown(); - } - - void _shutdown() { + bool update(Queue events, bool isDone) { if (_streamQueue._isDone) { _completer.complete(); } else { _streamQueue._ensureListening(); - _completer.complete(_streamQueue._dispose().cancel()); + _completer.complete(_streamQueue._extractStream().listen(null).cancel()); } + return true; } } @@ -559,21 +601,12 @@ class _RestRequest implements _EventRequest { /// The stream which will contain the remaining events of [_streamQueue]. Stream get stream => _completer.stream; - bool addEvents(Queue events) { - _completeStream(events); - return true; - } - - void close(Queue events) { - _completeStream(events); - } - - void _completeStream(Queue events) { + bool update(Queue events, bool isDone) { if (events.isEmpty) { if (_streamQueue._isDone) { _completer.setEmpty(); } else { - _completer.setSourceStream(_getRestStream()); + _completer.setSourceStream(_streamQueue._extractStream()); } } else { // There are prefetched events which needs to be added before the @@ -582,26 +615,11 @@ class _RestRequest implements _EventRequest { for (var event in events) { event.addTo(controller); } - controller.addStream(_getRestStream(), cancelOnError: false) + controller.addStream(_streamQueue._extractStream(), cancelOnError: false) .whenComplete(controller.close); _completer.setSourceStream(controller.stream); } - } - - /// Create a stream from the rest of [_streamQueue]'s subscription. - Stream _getRestStream() { - if (_streamQueue._isDone) { - var controller = new StreamController()..close(); - return controller.stream; - // TODO(lrn). Use the following when 1.11 is released. - // return new Stream.empty(); - } - if (_streamQueue._subscription == null) { - return _streamQueue._sourceStream; - } - var subscription = _streamQueue._dispose(); - subscription.resume(); - return new SubscriptionStream(subscription); + return true; } } @@ -616,15 +634,15 @@ class _HasNextRequest implements _EventRequest { Future get future => _completer.future; - bool addEvents(Queue events) { + bool update(Queue events, bool isDone) { if (events.isNotEmpty) { _completer.complete(true); return true; } + if (isDone) { + _completer.complete(false); + return true; + } return false; } - - void close(_) { - _completer.complete(false); - } } From ff13b513200446667a286f1a4d2f01554337b6c3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 8 Sep 2015 14:08:12 -0700 Subject: [PATCH 047/260] Add AsyncMemoizer.future. R=lrn@google.com Review URL: https://codereview.chromium.org//1324743003 . --- pkgs/async/CHANGELOG.md | 5 +++++ pkgs/async/lib/src/async_memoizer.dart | 11 ++++++----- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/async_memoizer_test.dart | 3 +++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 39d204b2..7f9ec485 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.4.0 + +- Added `AsyncMemoizer.future`, which allows the result to be accessed before + `runOnce()` is called. + ## 1.3.0 - Added `StreamCompleter` class for creating a stream now and providing its diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index 68a6c80e..41c3dae8 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -31,17 +31,18 @@ import 'dart:async'; class AsyncMemoizer { /// The future containing the method's result. /// - /// This will be `null` if [run] hasn't been called yet. - Future _future; + /// This can be accessed at any time, and will fire once [runOnce] is called. + Future get future => _completer.future; + final _completer = new Completer(); /// Whether [run] has been called yet. - bool get hasRun => _future != null; + bool get hasRun => _completer.isCompleted; /// Runs the function, [computation], if it hasn't been run before. /// /// If [run] has already been called, this returns the original result. Future runOnce(computation()) { - if (_future == null) _future = new Future.sync(computation); - return _future; + if (!hasRun) _completer.complete(new Future.sync(computation)); + return future; } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 84b8de5b..7062a992 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.3.0 +version: 1.4.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart index 3289aa96..b8ca38a1 100644 --- a/pkgs/async/test/async_memoizer_test.dart +++ b/pkgs/async/test/async_memoizer_test.dart @@ -21,16 +21,19 @@ main() { }); test("forwards the return value from the function", () async { + expect(cache.future, completion(equals("value"))); expect(cache.runOnce(() => "value"), completion(equals("value"))); expect(cache.runOnce(() {}), completion(equals("value"))); }); test("forwards the return value from an async function", () async { + expect(cache.future, completion(equals("value"))); expect(cache.runOnce(() async => "value"), completion(equals("value"))); expect(cache.runOnce(() {}), completion(equals("value"))); }); test("forwards the error from an async function", () async { + expect(cache.future, throwsA("error")); expect(cache.runOnce(() async => throw "error"), throwsA("error")); expect(cache.runOnce(() {}), throwsA("error")); }); From 4b97ee0b179bea7ea695f9af555e0768c75464e9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 8 Sep 2015 14:15:14 -0700 Subject: [PATCH 048/260] Add a CancelableFuture class. This is useful for controlling processes that the user may want a value out of or may want to cancel. R=lrn@google.com Review URL: https://codereview.chromium.org//1266603005 . --- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/cancelable_operation.dart | 148 +++++++++++++ .../async/test/cancelable_operation_test.dart | 197 ++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 pkgs/async/lib/src/cancelable_operation.dart create mode 100644 pkgs/async/test/cancelable_operation_test.dart diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 71a18492..07b418b1 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -6,6 +6,7 @@ library dart.pkg.async; export "result.dart"; export "src/async_memoizer.dart"; +export "src/cancelable_operation.dart"; export "src/delegate/event_sink.dart"; export "src/delegate/future.dart"; export "src/delegate/sink.dart"; diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart new file mode 100644 index 00000000..a7fe6a87 --- /dev/null +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -0,0 +1,148 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.cancelable_operation; + +import 'dart:async'; + +import 'package:async/async.dart'; + +/// An asynchronuos operation that can be cancelled. +/// +/// The value of this operation is exposed as [value]. When this operation is +/// cancelled, [value] won't complete either successfully or with an error. If +/// [value] has already completed, cancelling the operation does nothing. +class CancelableOperation { + /// The completer that produced this operation. + /// + /// This is canceled when [cancel] is called. + final CancelableCompleter _completer; + + CancelableOperation._(this._completer); + + /// Creates a [CancelableOperation] wrapping [inner]. + /// + /// When this operation is canceled, [onCancel] will be called and any value + /// or error produced by [inner] will be discarded. The callback may return a + /// Future to indicate that asynchronous work has to be done to cancel the + /// future; this Future will be returned by [cancel]. + /// + /// [onCancel] will be called synchronously when the operation is canceled. + /// It's guaranteed to only be called once. + factory CancelableOperation.fromFuture(Future inner, {onCancel()}) { + var completer = new CancelableCompleter(onCancel: onCancel); + completer.complete(inner); + return completer.operation; + } + + /// The value returned by the operation. + Future get value => _completer._inner.future; + + /// Creates a [Stream] containing the result of this operation. + /// + /// This is like `value.asStream()`, but if a subscription to the stream is + /// canceled, this is as well. + Stream asStream() { + var controller = new StreamController( + sync: true, onCancel: _completer._cancel); + + value.then((value) { + controller.add(value); + controller.close(); + }, onError: (error, stackTrace) { + controller.addError(error, stackTrace); + controller.close(); + }); + return controller.stream; + } + + /// Cancels this operation. + /// + /// This returns the [Future] returned by the [CancelableCompleter]'s + /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`. + Future cancel() => _completer._cancel(); +} + +/// A completer for a [CancelableOperation]. +class CancelableCompleter { + /// The completer for the wrapped future. + final Completer _inner; + + /// The callback to call if the future is canceled. + final ZoneCallback _onCancel; + + /// Creates a new completer for a [CancelableOperation]. + /// + /// When the future operation canceled, as long as the completer hasn't yet + /// completed, [onCancel] is called. The callback may return a [Future]; if + /// so, that [Future] is returned by [CancelableOperation.cancel]. + /// + /// [onCancel] will be called synchronously when the operation is canceled. + /// It's guaranteed to only be called once. + CancelableCompleter({onCancel()}) + : _onCancel = onCancel, + _inner = new Completer() { + _operation = new CancelableOperation._(this); + } + + /// The operation controlled by this completer. + CancelableOperation get operation => _operation; + CancelableOperation _operation; + + /// Whether the completer has completed. + bool get isCompleted => _isCompleted; + bool _isCompleted = false; + + /// Whether the completer was canceled before being completed. + bool get isCanceled => _isCanceled; + bool _isCanceled = false; + + /// The memoizer for [_cancel]. + final _cancelMemo = new AsyncMemoizer(); + + /// Completes [operation] to [value]. + /// + /// If [value] is a [Future], this will complete to the result of that + /// [Future] once it completes. + void complete([value]) { + if (_isCompleted) throw new StateError("Operation already completed"); + _isCompleted = true; + + if (value is! Future) { + if (_isCanceled) return; + _inner.complete(value); + return; + } + + if (_isCanceled) { + // Make sure errors from [value] aren't top-leveled. + value.catchError((_) {}); + return; + } + + value.then((result) { + if (_isCanceled) return; + _inner.complete(result); + }, onError: (error, stackTrace) { + if (_isCanceled) return; + _inner.completeError(error, stackTrace); + }); + } + + /// Completes [operation] to [error]. + void completeError(Object error, [StackTrace stackTrace]) { + if (_isCompleted) throw new StateError("Operation already completed"); + _isCompleted = true; + + if (_isCanceled) return; + _inner.completeError(error, stackTrace); + } + + /// Cancel the completer. + Future _cancel() => _cancelMemo.runOnce(() { + if (_inner.isCompleted) return null; + _isCanceled = true; + if (_onCancel != null) return _onCancel(); + }); +} diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart new file mode 100644 index 00000000..189c0738 --- /dev/null +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -0,0 +1,197 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + group("without being canceled", () { + var completer; + setUp(() { + completer = new CancelableCompleter( + onCancel: expectAsync(() {}, count: 0)); + }); + + test("sends values to the future", () { + expect(completer.operation.value, completion(equals(1))); + expect(completer.isCompleted, isFalse); + completer.complete(1); + expect(completer.isCompleted, isTrue); + }); + + test("sends errors to the future", () { + expect(completer.operation.value, throwsA("error")); + expect(completer.isCompleted, isFalse); + completer.completeError("error"); + expect(completer.isCompleted, isTrue); + }); + + test("sends values in a future to the future", () { + expect(completer.operation.value, completion(equals(1))); + expect(completer.isCompleted, isFalse); + completer.complete(new Future.value(1)); + expect(completer.isCompleted, isTrue); + }); + + test("sends errors in a future to the future", () { + expect(completer.operation.value, throwsA("error")); + expect(completer.isCompleted, isFalse); + completer.complete(new Future.error("error")); + expect(completer.isCompleted, isTrue); + }); + + group("throws a StateError if completed", () { + test("successfully twice", () { + completer.complete(1); + expect(() => completer.complete(1), throwsStateError); + }); + + test("successfully then unsuccessfully", () { + completer.complete(1); + expect(() => completer.completeError("error"), throwsStateError); + }); + + test("unsuccessfully twice", () { + expect(completer.operation.value, throwsA("error")); + completer.completeError("error"); + expect(() => completer.completeError("error"), throwsStateError); + }); + + test("successfully then with a future", () { + completer.complete(1); + expect(() => completer.complete(new Completer().future), + throwsStateError); + }); + + test("with a future then successfully", () { + completer.complete(new Completer().future); + expect(() => completer.complete(1), throwsStateError); + }); + + test("with a future twice", () { + completer.complete(new Completer().future); + expect(() => completer.complete(new Completer().future), + throwsStateError); + }); + }); + + group("CancelableOperation.fromFuture", () { + test("forwards values", () { + var operation = new CancelableOperation.fromFuture(new Future.value(1)); + expect(operation.value, completion(equals(1))); + }); + + test("forwards errors", () { + var operation = new CancelableOperation.fromFuture( + new Future.error("error")); + expect(operation.value, throwsA("error")); + }); + }); + }); + + group("when canceled", () { + test("causes the future never to fire", () async { + var completer = new CancelableCompleter(); + completer.operation.value.whenComplete(expectAsync(() {}, count: 0)); + completer.operation.cancel(); + + // Give the future plenty of time to fire if it's going to. + await flushMicrotasks(); + completer.complete(); + await flushMicrotasks(); + }); + + test("fires onCancel", () { + var canceled = false; + var completer; + completer = new CancelableCompleter(onCancel: expectAsync(() { + expect(completer.isCanceled, isTrue); + canceled = true; + })); + + expect(canceled, isFalse); + expect(completer.isCanceled, isFalse); + expect(completer.isCompleted, isFalse); + completer.operation.cancel(); + expect(canceled, isTrue); + expect(completer.isCanceled, isTrue); + expect(completer.isCompleted, isFalse); + }); + + test("returns the onCancel future each time cancel is called", () { + var completer = new CancelableCompleter(onCancel: expectAsync(() { + return new Future.value(1); + })); + expect(completer.operation.cancel(), completion(equals(1))); + expect(completer.operation.cancel(), completion(equals(1))); + expect(completer.operation.cancel(), completion(equals(1))); + }); + + test("returns a future even if onCancel doesn't", () { + var completer = new CancelableCompleter(onCancel: expectAsync(() {})); + expect(completer.operation.cancel(), completes); + }); + + test("doesn't call onCancel if the completer has completed", () { + var completer = new CancelableCompleter( + onCancel: expectAsync(() {}, count: 0)); + completer.complete(1); + expect(completer.operation.value, completion(equals(1))); + expect(completer.operation.cancel(), completes); + }); + + test("does call onCancel if the completer has completed to an unfired " + "Future", () { + var completer = new CancelableCompleter(onCancel: expectAsync(() {})); + completer.complete(new Completer().future); + expect(completer.operation.cancel(), completes); + }); + + test("doesn't call onCancel if the completer has completed to a fired " + "Future", () async { + var completer = new CancelableCompleter( + onCancel: expectAsync(() {}, count: 0)); + completer.complete(new Future.value(1)); + await completer.operation.value; + expect(completer.operation.cancel(), completes); + }); + + test("can be completed once after being canceled", () async { + var completer = new CancelableCompleter(); + completer.operation.value.whenComplete(expectAsync(() {}, count: 0)); + await completer.operation.cancel(); + completer.complete(1); + expect(() => completer.complete(1), throwsStateError); + }); + }); + + group("asStream()", () { + test("emits a value and then closes", () { + var completer = new CancelableCompleter(); + expect(completer.operation.asStream().toList(), completion(equals([1]))); + completer.complete(1); + }); + + test("emits an error and then closes", () { + var completer = new CancelableCompleter(); + var queue = new StreamQueue(completer.operation.asStream()); + expect(queue.next, throwsA("error")); + expect(queue.hasNext, completion(isFalse)); + completer.completeError("error"); + }); + + test("cancels the completer when the subscription is canceled", () { + var completer = new CancelableCompleter(onCancel: expectAsync(() {})); + var sub = completer.operation.asStream() + .listen(expectAsync((_) {}, count: 0)); + completer.operation.value.whenComplete(expectAsync(() {}, count: 0)); + sub.cancel(); + expect(completer.isCanceled, isTrue); + }); + }); +} From 6f6ffa1c45ab9f6a1ce72197656b06bdf8886ec1 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 13 Oct 2015 12:37:21 +0200 Subject: [PATCH 049/260] Fix timing of async test. When delayed by other tests, all the values didn't necessarily arrive before they were tested against. BUG= https://github.com/dart-lang/async/issues/4 R=floitsch@google.com Review URL: https://codereview.chromium.org//1390923006 . --- pkgs/async/test/subscription_stream_test.dart | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 03f0ddde..0d1870b2 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -73,8 +73,11 @@ main() { for (var sourceCancels in [false, true]) { group("${sourceCancels ? "yes" : "no"}:", () { var subscriptionStream; + var onCancel; // Completes if source stream is canceled before done. setUp(() { - var source = createErrorStream(); + var cancelCompleter = new Completer(); + var source = createErrorStream(cancelCompleter); + onCancel = cancelCompleter.future; var sourceSubscription = source.listen(null, cancelOnError: sourceCancels); subscriptionStream = new SubscriptionStream(sourceSubscription); @@ -89,9 +92,12 @@ main() { cancelOnError: false); var expected = [1, 2, "To err is divine!"]; if (sourceCancels) { - var timeout = done.future.timeout(const Duration(milliseconds: 5), - onTimeout: () => true); - expect(await timeout, true); + await onCancel; + // And [done] won't complete at all. + bool isDone = false; + done.future.then((_) { isDone = true; }); + await new Future.delayed(const Duration(milliseconds: 5)); + expect(isDone, false); } else { expected.add(4); await done.future; @@ -155,20 +161,25 @@ Stream createStream() async* { yield 4; } -Stream createErrorStream() { - StreamController controller = new StreamController(); - () async { - controller.add(1); +Stream createErrorStream([Completer onCancel]) async* { + bool canceled = true; + try { + yield 1; await flushMicrotasks(); - controller.add(2); + yield 2; await flushMicrotasks(); - controller.addError("To err is divine!"); + yield* new Future.error("To err is divine!").asStream(); await flushMicrotasks(); - controller.add(4); + yield 4; await flushMicrotasks(); - controller.close(); - }(); - return controller.stream; + canceled = false; + } finally { + // Completes before the "done", but should be after all events. + if (canceled && onCancel != null) { + await flushMicrotasks(); + onCancel.complete(); + } + } } Stream createLongStream() async* { From cdbe0bfceefe4084dab96393c51d00ef7c1747bf Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 28 Oct 2015 14:11:27 -0700 Subject: [PATCH 050/260] Add a RestartableTimer class. This is useful for heartbeat-style timeouts where a timeout is reset when certain actions occur. R=lrn@google.com Review URL: https://codereview.chromium.org//1417373004 . --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/restartable_timer.dart | 48 +++++++++ pkgs/async/pubspec.yaml | 3 +- pkgs/async/test/restartable_timer_test.dart | 110 ++++++++++++++++++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/restartable_timer.dart create mode 100644 pkgs/async/test/restartable_timer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 7f9ec485..5801872c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,9 @@ - Added `AsyncMemoizer.future`, which allows the result to be accessed before `runOnce()` is called. +- Added `RestartableTimer`, a non-periodic timer that can be reset over and + over. + ## 1.3.0 - Added `StreamCompleter` class for creating a stream now and providing its diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 07b418b1..9da3552b 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -14,6 +14,7 @@ export "src/delegate/stream_consumer.dart"; export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; export "src/future_group.dart"; +export "src/restartable_timer.dart"; export "src/result_future.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart new file mode 100644 index 00000000..05196d28 --- /dev/null +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.restartable_timer; + +import 'dart:async'; + +/// A non-periodic timer that can be restarted any number of times. +/// +/// Once restarted (via [reset]), the timer counts down from its original +/// duration again. +class RestartableTimer implements Timer { + /// The duration of the timer. + final Duration _duration; + + /// The callback to call when the timer fires. + final ZoneCallback _callback; + + /// The timer for the current or most recent countdown. + /// + /// This timer is canceled and overwritten every time this [RestartableTimer] + /// is reset. + Timer _timer; + + /// Creates a new timer. + /// + /// The [callback] function is invoked after the given [duration]. Unlike a + /// normal non-periodic [Timer], [callback] may be called more than once. + RestartableTimer(this._duration, this._callback) { + _timer = new Timer(_duration, _callback); + } + + bool get isActive => _timer.isActive; + + /// Restarts the timer so that it counts down from its original duration + /// again. + /// + /// This restarts the timer even if it has already fired or has been canceled. + void reset() { + _timer.cancel(); + _timer = new Timer(_duration, _callback); + } + + void cancel() { + _timer.cancel(); + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 7062a992..91d752d9 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,9 +1,10 @@ name: async -version: 1.4.0-dev +version: 1.4.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async dev_dependencies: + fake_async: "^0.1.2" stack_trace: "^1.0.0" test: "^0.12.0" environment: diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart new file mode 100644 index 00000000..6732b813 --- /dev/null +++ b/pkgs/async/test/restartable_timer_test.dart @@ -0,0 +1,110 @@ +// Copyright (c) 2015, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:fake_async/fake_async.dart'; +import 'package:test/test.dart'; + +main() { + test("runs the callback once the duration has elapsed", () { + new FakeAsync().run((async) { + var fired = false; + var timer = new RestartableTimer(new Duration(seconds: 5), () { + fired = true; + }); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + + async.elapse(new Duration(seconds: 1)); + expect(fired, isTrue); + }); + }); + + test("doesn't run the callback if the timer is canceled", () { + new FakeAsync().run((async) { + var fired = false; + var timer = new RestartableTimer(new Duration(seconds: 5), () { + fired = true; + }); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + timer.cancel(); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + }); + }); + + test("resets the duration if the timer is reset before it fires", () { + new FakeAsync().run((async) { + var fired = false; + var timer = new RestartableTimer(new Duration(seconds: 5), () { + fired = true; + }); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + timer.reset(); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + + async.elapse(new Duration(seconds: 1)); + expect(fired, isTrue); + }); + }); + + test("re-runs the callback if the timer is reset after firing", () { + new FakeAsync().run((async) { + var fired = 0; + var timer = new RestartableTimer(new Duration(seconds: 5), () { + fired++; + }); + + async.elapse(new Duration(seconds: 5)); + expect(fired, equals(1)); + timer.reset(); + + async.elapse(new Duration(seconds: 5)); + expect(fired, equals(2)); + timer.reset(); + + async.elapse(new Duration(seconds: 5)); + expect(fired, equals(3)); + }); + }); + + test("runs the callback if the timer is reset after being canceled", () { + new FakeAsync().run((async) { + var fired = false; + var timer = new RestartableTimer(new Duration(seconds: 5), () { + fired = true; + }); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + timer.cancel(); + + async.elapse(new Duration(seconds: 4)); + expect(fired, isFalse); + timer.reset(); + + async.elapse(new Duration(seconds: 5)); + expect(fired, isTrue); + }); + }); + + test("only runs the callback once if the timer isn't reset", () { + new FakeAsync().run((async) { + var timer = new RestartableTimer( + new Duration(seconds: 5), + expectAsync(() {}, count: 1)); + async.elapse(new Duration(seconds: 10)); + }); + }); +} From fa0d75d94e150b7e93d750b7b66999889f2ece45 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 5 Nov 2015 14:51:27 -0800 Subject: [PATCH 051/260] Mention CancelableCompleter in the CHANGELOG. R=lrn@google.com Review URL: https://codereview.chromium.org//1415393004 . --- pkgs/async/CHANGELOG.md | 3 +++ pkgs/async/lib/src/cancelable_operation.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 5801872c..4cdfc6ad 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,9 @@ - Added `AsyncMemoizer.future`, which allows the result to be accessed before `runOnce()` is called. +- Added `CancelableOperation`, an asynchronous operation that can be canceled. + It can be created using a `CancelableCompleter`. + - Added `RestartableTimer`, a non-periodic timer that can be reset over and over. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index a7fe6a87..89889a3e 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'package:async/async.dart'; -/// An asynchronuos operation that can be cancelled. +/// An asynchronous operation that can be cancelled. /// /// The value of this operation is exposed as [value]. When this operation is /// cancelled, [value] won't complete either successfully or with an error. If diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 91d752d9..8c02a7a7 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.4.0 +version: 1.4.1-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From b9add96a2f86a549599e289ddf88d8521296d644 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 14 Dec 2015 13:10:39 -0800 Subject: [PATCH 052/260] Add a LazyStream class. R=lrn@google.com Review URL: https://codereview.chromium.org//1491923005 . --- pkgs/async/CHANGELOG.md | 5 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/lazy_stream.dart | 51 ++++++++++ pkgs/async/lib/src/stream_splitter.dart | 1 - pkgs/async/lib/src/subscription_stream.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/lazy_stream_test.dart | 106 ++++++++++++++++++++ 7 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 pkgs/async/lib/src/lazy_stream.dart create mode 100644 pkgs/async/test/lazy_stream_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 4cdfc6ad..7e7ed4ad 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,10 @@ ## 1.4.0 +- Added `LazyStream`, which forwards to the return value of a callback that's + only called when the stream is listened to. + +## 1.4.0 + - Added `AsyncMemoizer.future`, which allows the result to be accessed before `runOnce()` is called. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 9da3552b..1d127fd6 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -14,6 +14,7 @@ export "src/delegate/stream_consumer.dart"; export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; export "src/future_group.dart"; +export "src/lazy_stream.dart"; export "src/restartable_timer.dart"; export "src/result_future.dart"; export "src/stream_completer.dart"; diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart new file mode 100644 index 00000000..1878d4cf --- /dev/null +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.lazy_stream; + +import "dart:async"; + +import "stream_completer.dart"; + +/// A [Stream] wrapper that forwards to another [Stream] that's initialized +/// lazily. +/// +/// This class allows a concrete `Stream` to be created only once it has a +/// listener. It's useful to wrapping APIs that do expensive computation to +/// produce a `Stream`. +class LazyStream extends Stream { + /// The callback that's called to create the inner stream. + ZoneCallback _callback; + + /// Creates a single-subscription `Stream` that calls [callback] when it gets + /// a listener and forwards to the returned stream. + /// + /// The [callback] may return a `Stream` or a `Future`. + LazyStream(callback()) : _callback = callback { + // Explicitly check for null because we null out [_callback] internally. + if (_callback == null) throw new ArgumentError.notNull('callback'); + } + + StreamSubscription listen(void onData(T event), + {Function onError, + void onDone(), + bool cancelOnError}) { + if (_callback == null) { + throw new StateError("Stream has already been listened to."); + } + + // Null out the callback before we invoke it to ensure that even while + // running it, this can't be called twice. + var callback = _callback; + _callback = null; + var result = callback(); + + Stream stream = result is Future + ? StreamCompleter.fromFuture(result) + : result; + + return stream.listen(onData, + onError: onError, onDone: onDone, cancelOnError: cancelOnError); + } +} diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index 6ec440f5..ac401a7b 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -5,7 +5,6 @@ library async.stream_splitter; import 'dart:async'; -import 'dart:collection'; import '../result.dart'; import 'future_group.dart'; diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index e9e7d77b..bdf329b9 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -10,7 +10,7 @@ import "delegate/stream_subscription.dart"; /// A [Stream] adapter for a [StreamSubscription]. /// -/// This class allows as `StreamSubscription` to be treated as a `Stream`. +/// This class allows a `StreamSubscription` to be treated as a `Stream`. /// /// The subscription is paused until the stream is listened to, /// then it is resumed and the events are passed on to the diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 8c02a7a7..e98c59ec 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.4.1-dev +version: 1.5.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart new file mode 100644 index 00000000..63f2a197 --- /dev/null +++ b/pkgs/async/test/lazy_stream_test.dart @@ -0,0 +1,106 @@ +// Copyright (c) 2015, 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. + +import "dart:async"; + +import "package:async/async.dart"; +import "package:test/test.dart"; + +import "utils.dart"; + +main() { + test("disallows a null callback", () { + expect(() => new LazyStream(null), throwsArgumentError); + }); + + test("calls the callback when the stream is listened", () async { + var callbackCalled = false; + var stream = new LazyStream(expectAsync(() { + callbackCalled = true; + return new Stream.empty(); + })); + + await flushMicrotasks(); + expect(callbackCalled, isFalse); + + stream.listen(null); + expect(callbackCalled, isTrue); + }); + + test("calls the callback when the stream is listened", () async { + var callbackCalled = false; + var stream = new LazyStream(expectAsync(() { + callbackCalled = true; + return new Stream.empty(); + })); + + await flushMicrotasks(); + expect(callbackCalled, isFalse); + + stream.listen(null); + expect(callbackCalled, isTrue); + }); + + test("forwards to a synchronously-provided stream", () async { + var controller = new StreamController(); + var stream = new LazyStream(expectAsync(() => controller.stream)); + + var events = []; + stream.listen(events.add); + + controller.add(1); + await flushMicrotasks(); + expect(events, equals([1])); + + controller.add(2); + await flushMicrotasks(); + expect(events, equals([1, 2])); + + controller.add(3); + await flushMicrotasks(); + expect(events, equals([1, 2, 3])); + + controller.close(); + }); + + test("forwards to an asynchronously-provided stream", () async { + var controller = new StreamController(); + var stream = new LazyStream(expectAsync(() async => controller.stream)); + + var events = []; + stream.listen(events.add); + + controller.add(1); + await flushMicrotasks(); + expect(events, equals([1])); + + controller.add(2); + await flushMicrotasks(); + expect(events, equals([1, 2])); + + controller.add(3); + await flushMicrotasks(); + expect(events, equals([1, 2, 3])); + + controller.close(); + }); + + test("a lazy stream can't be listened to multiple times", () { + var stream = new LazyStream(expectAsync(() => new Stream.empty())); + expect(stream.isBroadcast, isFalse); + + stream.listen(null); + expect(() => stream.listen(null), throwsStateError); + expect(() => stream.listen(null), throwsStateError); + }); + + test("a lazy stream can't be listened to from within its callback", () { + var stream; + stream = new LazyStream(expectAsync(() { + expect(() => stream.listen(null), throwsStateError); + return new Stream.empty(); + })); + stream.listen(null); + }); +} From ad8229607eec8b951e216393345b0c868e72ca4e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 7 Jan 2016 12:15:57 -0800 Subject: [PATCH 053/260] Add CancelableOperation.valueOrCancellation. R=lrn@google.com Review URL: https://codereview.chromium.org//1561323002 . --- pkgs/async/CHANGELOG.md | 7 +++- pkgs/async/lib/src/cancelable_operation.dart | 31 +++++++++++--- pkgs/async/pubspec.yaml | 2 +- .../async/test/cancelable_operation_test.dart | 42 +++++++++++++++++++ 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 7e7ed4ad..6db6f368 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,9 @@ -## 1.4.0 +## 1.6.0 + +- Added `CancelableOperation.valueOrCancellation()`, which allows users to be + notified when an operation is canceled elsewhere. + +## 1.5.0 - Added `LazyStream`, which forwards to the return value of a callback that's only called when the stream is listened to. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 89889a3e..a48c94fe 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -57,6 +57,24 @@ class CancelableOperation { return controller.stream; } + /// Creates a [Future] that completes when this operation completes *or* when + /// it's cancelled. + /// + /// If this operation completes, this completes to the same result as [value]. + /// If this operation is cancelled, the returned future waits for the future + /// returned by [cancel], then completes to [cancellationValue]. + Future valueOrCancellation([T cancellationValue]) { + var completer = new Completer.sync(); + + value.then(completer.complete, onError: completer.completeError); + + _completer._cancelMemo.future.then((_) { + completer.complete(cancellationValue); + }, onError: completer.completeError); + + return completer.future; + } + /// Cancels this operation. /// /// This returns the [Future] returned by the [CancelableCompleter]'s @@ -140,9 +158,12 @@ class CancelableCompleter { } /// Cancel the completer. - Future _cancel() => _cancelMemo.runOnce(() { - if (_inner.isCompleted) return null; - _isCanceled = true; - if (_onCancel != null) return _onCancel(); - }); + Future _cancel() { + if (_inner.isCompleted) return new Future.value(); + + return _cancelMemo.runOnce(() { + _isCanceled = true; + if (_onCancel != null) return _onCancel(); + }); + } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e98c59ec..16e2ea23 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.5.0 +version: 1.6.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 189c0738..4d8c295e 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -45,6 +45,16 @@ void main() { expect(completer.isCompleted, isTrue); }); + test("sends values to valueOrCancellation", () { + expect(completer.operation.valueOrCancellation(), completion(equals(1))); + completer.complete(1); + }); + + test("sends errors to valueOrCancellation", () { + expect(completer.operation.valueOrCancellation(), throwsA("error")); + completer.completeError("error"); + }); + group("throws a StateError if completed", () { test("successfully twice", () { completer.complete(1); @@ -168,6 +178,38 @@ void main() { completer.complete(1); expect(() => completer.complete(1), throwsStateError); }); + + test("fires valueOrCancellation with the given value", () { + var completer = new CancelableCompleter(); + expect(completer.operation.valueOrCancellation(1), completion(equals(1))); + completer.operation.cancel(); + }); + + test("pipes an error through valueOrCancellation", () { + var completer = new CancelableCompleter(onCancel: () { + throw "error"; + }); + expect(completer.operation.valueOrCancellation(1), throwsA("error")); + completer.operation.cancel(); + }); + + test("valueOrCancellation waits on the onCancel future", () async { + var innerCompleter = new Completer(); + var completer = new CancelableCompleter(onCancel: () => innerCompleter.future); + + var fired = false; + completer.operation.valueOrCancellation().then((_) { + fired = true; + }); + + completer.operation.cancel(); + await flushMicrotasks(); + expect(fired, isFalse); + + innerCompleter.complete(); + await flushMicrotasks(); + expect(fired, isTrue); + }); }); group("asStream()", () { From b9c96998afe192827ff97c97624603ba40115cc7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 11 Jan 2016 13:06:20 -0800 Subject: [PATCH 054/260] Add a StreamSinkTransformer class. This makes it easier to programmatically manipulate sinks. In particular, StreamSinkTransformer.fromStreamTransformer makes it easy to use existing encoders and codecs when emitting data. R=lrn@google.com Review URL: https://codereview.chromium.org//1566603002 . --- pkgs/async/CHANGELOG.md | 4 + pkgs/async/lib/async.dart | 1 + .../lib/src/stream_sink_transformer.dart | 50 ++++ .../handler_transformer.dart | 107 +++++++++ .../stream_transformer_wrapper.dart | 62 +++++ pkgs/async/pubspec.yaml | 2 +- .../test/stream_sink_transformer_test.dart | 218 ++++++++++++++++++ pkgs/async/test/utils.dart | 16 ++ 8 files changed, 459 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/stream_sink_transformer.dart create mode 100644 pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart create mode 100644 pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart create mode 100644 pkgs/async/test/stream_sink_transformer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 6db6f368..8cb7880c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,10 @@ - Added `CancelableOperation.valueOrCancellation()`, which allows users to be notified when an operation is canceled elsewhere. +- Added `StreamSinkTransformer` which transforms events before they're passed to + a `StreamSink`, similarly to how `StreamTransformer` transforms events after + they're emitted by a stream. + ## 1.5.0 - Added `LazyStream`, which forwards to the return value of a callback that's diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 1d127fd6..8f14baca 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -20,6 +20,7 @@ export "src/result_future.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; export "src/stream_queue.dart"; +export "src/stream_sink_transformer.dart"; export "src/stream_splitter.dart"; export "src/subscription_stream.dart"; export "stream_zip.dart"; diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart new file mode 100644 index 00000000..a8410ab2 --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_sink_transformer; + +import 'dart:async'; + +import 'stream_sink_transformer/handler_transformer.dart'; +import 'stream_sink_transformer/stream_transformer_wrapper.dart'; + +/// A [StreamSinkTransformer] transforms the events being passed to a sink. +/// +/// This works on the same principle as a [StreamTransformer]. Each transformer +/// defines a [bind] method that takes in the original [StreamSink] and returns +/// the transformed version. However, where a [StreamTransformer] transforms +/// events after they leave the stream, this transforms them before they enter +/// the sink. +/// +/// Transformers must be able to have `bind` called used multiple times. +abstract class StreamSinkTransformer { + /// Creates a [StreamSinkTransformer] that transforms events and errors + /// using [transformer]. + /// + /// This is equivalent to piping all events from the outer sink through a + /// stream transformed by [transformer] and from there into the inner sink. + const factory StreamSinkTransformer.fromStreamTransformer( + StreamTransformer transformer) = + StreamTransformerWrapper; + + /// Creates a [StreamSinkTransformer] that delegates events to the given + /// handlers. + /// + /// The handlers work exactly as they do for [StreamTransformer.fromHandlers]. + /// They're called for each incoming event, and any actions on the sink + /// they're passed are forwarded to the inner sink. If a handler is omitted, + /// the event is passed through unaltered. + factory StreamSinkTransformer.fromHandlers({ + void handleData(S data, EventSink sink), + void handleError(Object error, StackTrace stackTrace, EventSink sink), + void handleDone(EventSink sink)}) { + return new HandlerTransformer(handleData, handleError, handleDone); + } + + /// Transforms the events passed to [sink]. + /// + /// Creates a new sink. When events are passed to the returned sink, it will + /// transform them and pass the transformed versions to [sink]. + StreamSink bind(StreamSink sink); +} diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart new file mode 100644 index 00000000..e5d8e3c6 --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -0,0 +1,107 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_sink_transformer.handler_transformer; + +import 'dart:async'; + +import '../stream_sink_transformer.dart'; +import '../delegate/stream_sink.dart'; + +/// The type of the callback for handling data events. +typedef void HandleData(S data, EventSink sink); + +/// The type of the callback for handling error events. +typedef void HandleError( + Object error, StackTrace stackTrace, EventSink sink); + +/// The type of the callback for handling done events. +typedef void HandleDone(EventSink sink); + +/// A [StreamSinkTransformer] that delegates events to the given handlers. +class HandlerTransformer implements StreamSinkTransformer { + /// The handler for data events. + final HandleData _handleData; + + /// The handler for error events. + final HandleError _handleError; + + /// The handler for done events. + final HandleDone _handleDone; + + HandlerTransformer( + this._handleData, this._handleError, this._handleDone); + + StreamSink bind(StreamSink sink) => new _HandlerSink(this, sink); +} + +/// A sink created by [HandlerTransformer]. +class _HandlerSink implements StreamSink { + /// The transformer that created this sink. + final HandlerTransformer _transformer; + + /// The original sink that's being transformed. + final StreamSink _inner; + + /// The wrapper for [_inner] whose [StreamSink.close] method can't emit + /// errors. + final StreamSink _safeCloseInner; + + Future get done => _inner.done; + + _HandlerSink(this._transformer, StreamSink inner) + : _inner = inner, + _safeCloseInner = new _SafeCloseSink(inner); + + void add(S event) { + if (_transformer._handleData == null) { + // [event] is an S and [_inner.add] takes a T. This style of conversion + // will throw an error in checked mode if [_inner] is actually a + // [StreamSink], but will work if [_inner] isn't reified and won't add + // an extra check in unchecked mode. + _inner.add(event as dynamic); + } else { + _transformer._handleData(event, _safeCloseInner); + } + } + + void addError(error, [StackTrace stackTrace]) { + if (_transformer._handleError == null) { + _inner.addError(error, stackTrace); + } else { + _transformer._handleError(error, stackTrace, _safeCloseInner); + } + } + + Future addStream(Stream stream) { + return _inner.addStream(stream.transform( + new StreamTransformer.fromHandlers( + handleData: _transformer._handleData, + handleError: _transformer._handleError, + handleDone: _closeSink))); + } + + Future close() { + if (_transformer._handleDone == null) return _inner.close(); + + _transformer._handleDone(_safeCloseInner); + return _inner.done; + } +} + +/// A wrapper for [StreamSink]s that swallows any errors returned by [close]. +/// +/// [HandlerTransformer] passes this to its handlers to ensure that when they +/// call [close], they don't leave any dangling [Future]s behind that might emit +/// unhandleable errors. +class _SafeCloseSink extends DelegatingStreamSink { + _SafeCloseSink(StreamSink inner) : super(inner); + + Future close() => super.close().catchError((_) {}); +} + +/// A function to pass as a [StreamTransformer]'s `handleDone` callback. +void _closeSink(EventSink sink) { + sink.close(); +} diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart new file mode 100644 index 00000000..b53f2086 --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -0,0 +1,62 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_sink_transformer.stream_transformer_wrapper; + +import 'dart:async'; + +import '../stream_sink_transformer.dart'; + +/// A [StreamSinkTransformer] that wraps a pre-existing [StreamTransformer]. +class StreamTransformerWrapper implements StreamSinkTransformer { + /// The wrapped transformer. + final StreamTransformer _transformer; + + const StreamTransformerWrapper(this._transformer); + + StreamSink bind(StreamSink sink) => + new _StreamTransformerWrapperSink(_transformer, sink); +} + +/// A sink created by [StreamTransformerWrapper]. +class _StreamTransformerWrapperSink implements StreamSink { + /// The controller through which events are passed. + /// + /// This is used to create a stream that can be transformed by the wrapped + /// transformer. + final _controller = new StreamController(sync: true); + + /// The original sink that's being transformed. + final StreamSink _inner; + + Future get done => _inner.done; + + _StreamTransformerWrapperSink(StreamTransformer transformer, + this._inner) { + _controller.stream.transform(transformer).listen( + _inner.add, + onError: _inner.addError, + onDone: () { + // Ignore any errors that come from this call to [_inner.close]. The + // user can access them through [done] or the value returned from + // [this.close], and we don't want them to get top-leveled. + _inner.close().catchError((_) {}); + }); + } + + void add(S event) { + _controller.add(event); + } + + void addError(error, [StackTrace stackTrace]) { + _controller.addError(error, stackTrace); + } + + Future addStream(Stream stream) => _controller.addStream(stream); + + Future close() { + _controller.close(); + return _inner.done; + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 16e2ea23..e13f6ab6 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.6.0-dev +version: 1.6.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart new file mode 100644 index 00000000..70e7e6cb --- /dev/null +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -0,0 +1,218 @@ +// Copyright (c) 2015, 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 filevents. + +import "dart:async"; + +import "package:async/async.dart"; +import "package:test/test.dart"; + +import "utils.dart"; + +void main() { + var controller; + setUp(() { + controller = new StreamController(); + }); + + group("fromStreamTransformer", () { + test("transforms data events", () { + var transformer = new StreamSinkTransformer.fromStreamTransformer( + new StreamTransformer.fromHandlers(handleData: (i, sink) { + sink.add(i * 2); + })); + var sink = transformer.bind(controller.sink); + + var results = []; + controller.stream.listen(results.add, onDone: expectAsync(() { + expect(results, equals([2, 4, 6])); + })); + + sink.add(1); + sink.add(2); + sink.add(3); + sink.close(); + }); + + test("transforms error events", () { + var transformer = new StreamSinkTransformer.fromStreamTransformer( + new StreamTransformer.fromHandlers( + handleError: (i, stackTrace, sink) { + sink.addError(i * 2, stackTrace); + })); + var sink = transformer.bind(controller.sink); + + var results = []; + controller.stream.listen(expectAsync((_) {}, count: 0), + onError: (error, stackTrace) { + results.add(error); + }, + onDone: expectAsync(() { + expect(results, equals([2, 4, 6])); + })); + + sink.addError(1); + sink.addError(2); + sink.addError(3); + sink.close(); + }); + + test("transforms done events", () { + var transformer = new StreamSinkTransformer.fromStreamTransformer( + new StreamTransformer.fromHandlers( + handleDone: (sink) { + sink.add(1); + sink.close(); + })); + var sink = transformer.bind(controller.sink); + + var results = []; + controller.stream.listen(results.add, onDone: expectAsync(() { + expect(results, equals([1])); + })); + + sink.close(); + }); + + test("forwards the future from inner.close", () async { + var transformer = new StreamSinkTransformer.fromStreamTransformer( + new StreamTransformer.fromHandlers()); + var innerSink = new CompleterStreamSink(); + var sink = transformer.bind(innerSink); + + // The futures shouldn't complete until the inner sink's close future + // completes. + var doneResult = new ResultFuture(sink.done); + doneResult.catchError((_) {}); + var closeResult = new ResultFuture(sink.close()); + closeResult.catchError((_) {}); + await flushMicrotasks(); + expect(doneResult.isComplete, isFalse); + expect(closeResult.isComplete, isFalse); + + // Once the inner sink is completed, the futures should fire. + innerSink.completer.complete(); + await flushMicrotasks(); + expect(doneResult.isComplete, isTrue); + expect(closeResult.isComplete, isTrue); + }); + + test("doesn't top-level the future from inner.close", () async { + var transformer = new StreamSinkTransformer.fromStreamTransformer( + new StreamTransformer.fromHandlers(handleData: (_, sink) { + sink.close(); + })); + var innerSink = new CompleterStreamSink(); + var sink = transformer.bind(innerSink); + + // This will close the inner sink, but it shouldn't top-level the error. + sink.add(1); + innerSink.completer.completeError("oh no"); + await flushMicrotasks(); + + // The error should be piped through done and close even if they're called + // after the underlying sink is closed. + expect(sink.done, throwsA("oh no")); + expect(sink.close(), throwsA("oh no")); + }); + }); + + group("fromHandlers", () { + test("transforms data events", () { + var transformer = new StreamSinkTransformer.fromHandlers( + handleData: (i, sink) { + sink.add(i * 2); + }); + var sink = transformer.bind(controller.sink); + + var results = []; + controller.stream.listen(results.add, onDone: expectAsync(() { + expect(results, equals([2, 4, 6])); + })); + + sink.add(1); + sink.add(2); + sink.add(3); + sink.close(); + }); + + test("transforms error events", () { + var transformer = new StreamSinkTransformer.fromHandlers( + handleError: (i, stackTrace, sink) { + sink.addError(i * 2, stackTrace); + }); + var sink = transformer.bind(controller.sink); + + var results = []; + controller.stream.listen(expectAsync((_) {}, count: 0), + onError: (error, stackTrace) { + results.add(error); + }, + onDone: expectAsync(() { + expect(results, equals([2, 4, 6])); + })); + + sink.addError(1); + sink.addError(2); + sink.addError(3); + sink.close(); + }); + + test("transforms done events", () { + var transformer = new StreamSinkTransformer.fromHandlers( + handleDone: (sink) { + sink.add(1); + sink.close(); + }); + var sink = transformer.bind(controller.sink); + + var results = []; + controller.stream.listen(results.add, onDone: expectAsync(() { + expect(results, equals([1])); + })); + + sink.close(); + }); + + test("forwards the future from inner.close", () async { + var transformer = new StreamSinkTransformer.fromHandlers(); + var innerSink = new CompleterStreamSink(); + var sink = transformer.bind(innerSink); + + // The futures shouldn't complete until the inner sink's close future + // completes. + var doneResult = new ResultFuture(sink.done); + doneResult.catchError((_) {}); + var closeResult = new ResultFuture(sink.close()); + closeResult.catchError((_) {}); + await flushMicrotasks(); + expect(doneResult.isComplete, isFalse); + expect(closeResult.isComplete, isFalse); + + // Once the inner sink is completed, the futures should fire. + innerSink.completer.complete(); + await flushMicrotasks(); + expect(doneResult.isComplete, isTrue); + expect(closeResult.isComplete, isTrue); + }); + + test("doesn't top-level the future from inner.close", () async { + var transformer = new StreamSinkTransformer.fromHandlers( + handleData: (_, sink) { + sink.close(); + }); + var innerSink = new CompleterStreamSink(); + var sink = transformer.bind(innerSink); + + // This will close the inner sink, but it shouldn't top-level the error. + sink.add(1); + innerSink.completer.completeError("oh no"); + await flushMicrotasks(); + + // The error should be piped through done and close even if they're called + // after the underlying sink is closed. + expect(sink.done, throwsA("oh no")); + expect(sink.close(), throwsA("oh no")); + }); + }); +} diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index b2ea885a..0dc47fa1 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -24,3 +24,19 @@ class UnusableStream extends Stream { throw new UnimplementedError("Gotcha!"); } } + +/// A dummy [StreamSink] for testing the routing of the [done] and [close] +/// futures. +/// +/// The [completer] field allows the user to control the future returned by +/// [done] and [close]. +class CompleterStreamSink implements StreamSink { + final completer = new Completer(); + + Future get done => completer.future; + + void add(T event) {} + void addError(error, [StackTrace stackTrace]) {} + Future addStream(Stream stream) async {} + Future close() => completer.future; +} From 1c95ec1ccfccae2625304e06ebc7fad90ed6a627 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 19 Jan 2016 13:47:59 -0800 Subject: [PATCH 055/260] Add SingleSubscriptionTransformer. This transforms a broadcast stream to a single-subscription stream, adding internal buffering. R=lrn@google.com Review URL: https://codereview.chromium.org//1584023002 . --- pkgs/async/CHANGELOG.md | 5 ++ pkgs/async/lib/async.dart | 1 + .../src/single_subscription_transformer.dart | 25 ++++++++++ pkgs/async/pubspec.yaml | 2 +- .../single_subscription_transformer_test.dart | 50 +++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/single_subscription_transformer.dart create mode 100644 pkgs/async/test/single_subscription_transformer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 8cb7880c..d7142dac 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.7.0 + +- Added `SingleSubscriptionTransformer`, a `StreamTransformer` that converts a + broadcast stream into a single-subscription stream. + ## 1.6.0 - Added `CancelableOperation.valueOrCancellation()`, which allows users to be diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 8f14baca..ab630dd5 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -17,6 +17,7 @@ export "src/future_group.dart"; export "src/lazy_stream.dart"; export "src/restartable_timer.dart"; export "src/result_future.dart"; +export "src/single_subscription_transformer.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; export "src/stream_queue.dart"; diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart new file mode 100644 index 00000000..28fd512d --- /dev/null +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.single_subscription_transformer; + +import 'dart:async'; + +/// A transformer that converts a broadcast stream into a single-subscription +/// stream. +/// +/// This buffers the broadcast stream's events, which means that it starts +/// listening to a stream as soon as it's bound. +class SingleSubscriptionTransformer implements StreamTransformer { + const SingleSubscriptionTransformer(); + + Stream bind(Stream stream) { + var subscription; + var controller = new StreamController(sync: true, + onCancel: () => subscription.cancel()); + subscription = stream.listen(controller.add, + onError: controller.addError, onDone: controller.close); + return controller.stream; + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e13f6ab6..423f3ce6 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.6.0 +version: 1.7.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/single_subscription_transformer_test.dart b/pkgs/async/test/single_subscription_transformer_test.dart new file mode 100644 index 00000000..f21d4e9a --- /dev/null +++ b/pkgs/async/test/single_subscription_transformer_test.dart @@ -0,0 +1,50 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + test("buffers events as soon as it's bound", () async { + var controller = new StreamController.broadcast(); + var stream = controller.stream.transform( + const SingleSubscriptionTransformer()); + + // Add events before [stream] has a listener to be sure it buffers them. + controller.add(1); + controller.add(2); + await flushMicrotasks(); + + expect(stream.toList(), completion(equals([1, 2, 3, 4]))); + await flushMicrotasks(); + + controller.add(3); + controller.add(4); + controller.close(); + }); + + test("cancels the subscription to the broadcast stream when it's canceled", + () async { + var canceled = false; + var controller = new StreamController.broadcast(onCancel: () { + canceled = true; + }); + var stream = controller.stream.transform( + const SingleSubscriptionTransformer()); + await flushMicrotasks(); + expect(canceled, isFalse); + + var subscription = stream.listen(null); + await flushMicrotasks(); + expect(canceled, isFalse); + + subscription.cancel(); + await flushMicrotasks(); + expect(canceled, isTrue); + }); +} From 91ab6194c382b7b4b1753bd9f07c6d3b27a50721 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 21 Jan 2016 12:59:25 -0800 Subject: [PATCH 056/260] Add a StreamSinkCompleter class. R=lrn@google.com Review URL: https://codereview.chromium.org//1616543002 . --- pkgs/async/CHANGELOG.md | 5 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/stream_sink_completer.dart | 151 +++++++++++ pkgs/async/pubspec.yaml | 2 +- .../test/stream_sink_completer_test.dart | 255 ++++++++++++++++++ pkgs/async/test/utils.dart | 45 ++++ 6 files changed, 458 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/stream_sink_completer.dart create mode 100644 pkgs/async/test/stream_sink_completer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d7142dac..b53d7552 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.8.0 + +- Added `StreamSinkCompleter`, for creating a `StreamSink` now and providing its + destination later as another sink. + ## 1.7.0 - Added `SingleSubscriptionTransformer`, a `StreamTransformer` that converts a diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index ab630dd5..ef59b73a 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -21,6 +21,7 @@ export "src/single_subscription_transformer.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; export "src/stream_queue.dart"; +export "src/stream_sink_completer.dart"; export "src/stream_sink_transformer.dart"; export "src/stream_splitter.dart"; export "src/subscription_stream.dart"; diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart new file mode 100644 index 00000000..10caa06b --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -0,0 +1,151 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.stream_sink_completer; + +import 'dart:async'; + +/// A [sink] where the destination is provided later. +/// +/// The [sink] is a normal sink that you can add events to to immediately, but +/// until [setDestinationSink] is called, the events will be buffered. +/// +/// The same effect can be achieved by using a [StreamController] and adding it +/// to the sink using [Sink.addStream] when the destination sink is ready. This +/// class attempts to shortcut some of the overhead when possible. For example, +/// if the [sink] only has events added after the destination sink has been set, +/// those events are added directly to the sink. +class StreamSinkCompleter { + /// The sink for this completer. + /// + /// When a destination sink is provided, events that have been passed to the + /// sink will be forwarded to the destination. + /// + /// Events can be added to the sink either before or after a destination sink + /// is set. + final StreamSink sink = new _CompleterSink(); + + /// Returns [sink] typed as a [_CompleterSink]. + _CompleterSink get _sink => sink; + + /// Sets a sink as the destination for events from the [StreamSinkCompleter]'s + /// [sink]. + /// + /// The completer's [sink] will act exactly as [destinationSink]. + /// + /// If the destination sink is set before events are added to [sink], further + /// events are forwarded directly to [destinationSink]. + /// + /// If events are added to [sink] before setting the destination sink, they're + /// buffered until the destination is available. + /// + /// A destination sink may be set at most once. + void setDestinationSink(StreamSink destinationSink) { + if (_sink._destinationSink != null) { + throw new StateError("Destination sink already set"); + } + _sink._setDestinationSink(destinationSink); + } +} + +/// [StreamSink] completed by [StreamSinkCompleter]. +class _CompleterSink implements StreamSink { + /// Controller for an intermediate sink. + /// + /// Created if the user adds events to this sink before the destination sink + /// is set. + StreamController _controller; + + /// Completer for [done]. + /// + /// Created if the user requests the [done] future before the destination sink + /// is set. + Completer _doneCompleter; + + /// Destination sink for the events added to this sink. + /// + /// Set when [StreamSinkCompleter.setDestinationSink] is called. + StreamSink _destinationSink; + + /// Whether events should be sent directly to [_destinationSink], as opposed + /// to going through [_controller]. + bool get _canSendDirectly => _controller == null && _destinationSink != null; + + Future get done { + if (_doneCompleter != null) return _doneCompleter.future; + if (_destinationSink == null) { + _doneCompleter = new Completer.sync(); + return _doneCompleter.future; + } + return _destinationSink.done; + } + + void add(T event) { + if (_canSendDirectly) { + _destinationSink.add(event); + } else { + _ensureController(); + _controller.add(event); + } + } + + void addError(error, [StackTrace stackTrace]) { + if (_canSendDirectly) { + _destinationSink.addError(error, stackTrace); + } else { + _ensureController(); + _controller.addError(error, stackTrace); + } + } + + Future addStream(Stream stream) { + if (_canSendDirectly) return _destinationSink.addStream(stream); + + _ensureController(); + return _controller.addStream(stream, cancelOnError: false); + } + + Future close() { + if (_canSendDirectly) { + _destinationSink.close(); + } else { + _ensureController(); + _controller.close(); + } + return done; + } + + /// Create [_controller] if it doesn't yet exist. + void _ensureController() { + if (_controller == null) _controller = new StreamController(sync: true); + } + + /// Sets the destination sink to which events from this sink will be provided. + /// + /// If set before the user adds events, events will be added directly to the + /// destination sink. If the user adds events earlier, an intermediate sink is + /// created using a stream controller, and the destination sink is linked to + /// it later. + void _setDestinationSink(StreamSink sink) { + assert(_destinationSink == null); + _destinationSink = sink; + + // If the user has already added data, it's buffered in the controller, so + // we add it to the sink. + if (_controller != null) { + // Catch any error that may come from [addStream] or [sink.close]. They'll + // be reported through [done] anyway. + sink + .addStream(_controller.stream) + .whenComplete(sink.close) + .catchError((_) {}); + } + + // If the user has already asked when the sink is done, connect the sink's + // done callback to that completer. + if (_doneCompleter != null) { + _doneCompleter.complete(sink.done); + } + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 423f3ce6..ccfa970d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.7.0 +version: 1.8.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart new file mode 100644 index 00000000..3892d267 --- /dev/null +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -0,0 +1,255 @@ +// Copyright (c) 2016, 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. + +import "dart:async"; + +import "package:async/async.dart"; +import "package:test/test.dart"; + +import "utils.dart"; + +main() { + var completer; + setUp(() { + completer = new StreamSinkCompleter(); + }); + + group("when a stream is linked before events are added", () { + test("data events are forwarded", () { + var sink = new TestSink(); + completer.setDestinationSink(sink); + completer.sink..add(1)..add(2)..add(3)..add(4); + + expect(sink.results[0].asValue.value, equals(1)); + expect(sink.results[1].asValue.value, equals(2)); + expect(sink.results[2].asValue.value, equals(3)); + expect(sink.results[3].asValue.value, equals(4)); + }); + + test("error events are forwarded", () { + var sink = new TestSink(); + completer.setDestinationSink(sink); + completer.sink..addError("oh no")..addError("that's bad"); + + expect(sink.results[0].asError.error, equals("oh no")); + expect(sink.results[1].asError.error, equals("that's bad")); + }); + + test("addStream is forwarded", () async { + var sink = new TestSink(); + completer.setDestinationSink(sink); + + var controller = new StreamController(); + completer.sink.addStream(controller.stream); + + controller.add(1); + controller.addError("oh no"); + controller.add(2); + controller.addError("that's bad"); + await flushMicrotasks(); + + expect(sink.results[0].asValue.value, equals(1)); + expect(sink.results[1].asError.error, equals("oh no")); + expect(sink.results[2].asValue.value, equals(2)); + expect(sink.results[3].asError.error, equals("that's bad")); + expect(sink.isClosed, isFalse); + + controller.close(); + await flushMicrotasks(); + expect(sink.isClosed, isFalse); + }); + + test("close() is forwarded", () { + var sink = new TestSink(); + completer.setDestinationSink(sink); + completer.sink.close(); + expect(sink.isClosed, isTrue); + }); + + test("the future from the inner close() is returned", () async { + var closeCompleter = new Completer(); + var sink = new TestSink(onDone: () => closeCompleter.future); + completer.setDestinationSink(sink); + + var closeCompleted = false; + completer.sink.close().then(expectAsync((_) { + closeCompleted = true; + })); + + await flushMicrotasks(); + expect(closeCompleted, isFalse); + + closeCompleter.complete(); + await flushMicrotasks(); + expect(closeCompleted, isTrue); + }); + + test("errors are forwarded from the inner close()", () { + var sink = new TestSink(onDone: () => throw "oh no"); + completer.setDestinationSink(sink); + expect(completer.sink.done, throwsA("oh no")); + expect(completer.sink.close(), throwsA("oh no")); + }); + + test("errors aren't top-leveled if only close() is listened to", () async { + var sink = new TestSink(onDone: () => throw "oh no"); + completer.setDestinationSink(sink); + expect(completer.sink.close(), throwsA("oh no")); + + // Give the event loop a chance to top-level errors if it's going to. + await flushMicrotasks(); + }); + + test("errors aren't top-leveled if only done is listened to", () async { + var sink = new TestSink(onDone: () => throw "oh no"); + completer.setDestinationSink(sink); + completer.sink.close(); + expect(completer.sink.done, throwsA("oh no")); + + // Give the event loop a chance to top-level errors if it's going to. + await flushMicrotasks(); + }); + }); + + group("when a stream is linked after events are added", () { + test("data events are forwarded", () async { + completer.sink..add(1)..add(2)..add(3)..add(4); + await flushMicrotasks(); + + var sink = new TestSink(); + completer.setDestinationSink(sink); + await flushMicrotasks(); + + expect(sink.results[0].asValue.value, equals(1)); + expect(sink.results[1].asValue.value, equals(2)); + expect(sink.results[2].asValue.value, equals(3)); + expect(sink.results[3].asValue.value, equals(4)); + }); + + test("error events are forwarded", () async { + completer.sink..addError("oh no")..addError("that's bad"); + await flushMicrotasks(); + + var sink = new TestSink(); + completer.setDestinationSink(sink); + await flushMicrotasks(); + + expect(sink.results[0].asError.error, equals("oh no")); + expect(sink.results[1].asError.error, equals("that's bad")); + }); + + test("addStream is forwarded", () async { + var controller = new StreamController(); + completer.sink.addStream(controller.stream); + + controller.add(1); + controller.addError("oh no"); + controller.add(2); + controller.addError("that's bad"); + controller.close(); + await flushMicrotasks(); + + var sink = new TestSink(); + completer.setDestinationSink(sink); + await flushMicrotasks(); + + expect(sink.results[0].asValue.value, equals(1)); + expect(sink.results[1].asError.error, equals("oh no")); + expect(sink.results[2].asValue.value, equals(2)); + expect(sink.results[3].asError.error, equals("that's bad")); + expect(sink.isClosed, isFalse); + }); + + test("close() is forwarded", () async { + completer.sink.close(); + await flushMicrotasks(); + + var sink = new TestSink(); + completer.setDestinationSink(sink); + await flushMicrotasks(); + + expect(sink.isClosed, isTrue); + }); + + test("the future from the inner close() is returned", () async { + var closeCompleted = false; + completer.sink.close().then(expectAsync((_) { + closeCompleted = true; + })); + await flushMicrotasks(); + + var closeCompleter = new Completer(); + var sink = new TestSink(onDone: () => closeCompleter.future); + completer.setDestinationSink(sink); + await flushMicrotasks(); + expect(closeCompleted, isFalse); + + closeCompleter.complete(); + await flushMicrotasks(); + expect(closeCompleted, isTrue); + }); + + test("errors are forwarded from the inner close()", () async { + expect(completer.sink.done, throwsA("oh no")); + expect(completer.sink.close(), throwsA("oh no")); + await flushMicrotasks(); + + var sink = new TestSink(onDone: () => throw "oh no"); + completer.setDestinationSink(sink); + }); + + test("errors aren't top-leveled if only close() is listened to", () async { + expect(completer.sink.close(), throwsA("oh no")); + await flushMicrotasks(); + + var sink = new TestSink(onDone: () => throw "oh no"); + completer.setDestinationSink(sink); + + // Give the event loop a chance to top-level errors if it's going to. + await flushMicrotasks(); + }); + + test("errors aren't top-leveled if only done is listened to", () async { + completer.sink.close(); + expect(completer.sink.done, throwsA("oh no")); + await flushMicrotasks(); + + var sink = new TestSink(onDone: () => throw "oh no"); + completer.setDestinationSink(sink); + + // Give the event loop a chance to top-level errors if it's going to. + await flushMicrotasks(); + }); + }); + + test("the sink is closed, the destination is set, then done is read", + () async { + expect(completer.sink.close(), completes); + await flushMicrotasks(); + + completer.setDestinationSink(new TestSink()); + await flushMicrotasks(); + + expect(completer.sink.done, completes); + }); + + test("done is read, the destination is set, then the sink is closed", + () async { + expect(completer.sink.done, completes); + await flushMicrotasks(); + + completer.setDestinationSink(new TestSink()); + await flushMicrotasks(); + + expect(completer.sink.close(), completes); + }); + + test("doesn't allow the destination sink to be set multiple times", () { + completer.setDestinationSink(new TestSink()); + expect(() => completer.setDestinationSink(new TestSink()), + throwsStateError); + expect(() => completer.setDestinationSink(new TestSink()), + throwsStateError); + }); +} diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 0dc47fa1..445b9fcf 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -6,6 +6,8 @@ library async.test.util; import "dart:async"; + +import "package:async/async.dart"; import "package:test/test.dart"; /// A zero-millisecond timer should wait until after all microtasks. @@ -40,3 +42,46 @@ class CompleterStreamSink implements StreamSink { Future addStream(Stream stream) async {} Future close() => completer.future; } + +/// A [StreamSink] that collects all events added to it as results. +/// +/// This is used for testing code that interacts with sinks. +class TestSink implements StreamSink { + /// The results corresponding to events that have been added to the sink. + final results = >[]; + + /// Whether [close] has been called. + bool get isClosed => _isClosed; + var _isClosed = false; + + Future get done => _doneCompleter.future; + final _doneCompleter = new Completer(); + + final Function _onDone; + + /// Creates a new sink. + /// + /// If [onDone] is passed, it's called when the user calls [close]. Its result + /// is piped to the [done] future. + TestSink({onDone()}) : _onDone = onDone ?? (() {}); + + void add(T event) { + results.add(new Result.value(event)); + } + + void addError(error, [StackTrace stackTrace]) { + results.add(new Result.error(error, stackTrace)); + } + + Future addStream(Stream stream) { + var completer = new Completer.sync(); + stream.listen(add, onError: addError, onDone: completer.complete); + return completer.future; + } + + Future close() { + _isClosed = true; + _doneCompleter.complete(new Future.microtask(_onDone)); + return done; + } +} From a243c56b66d7630dec287fc55c7e531226e1b403 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 21 Jan 2016 13:01:27 -0800 Subject: [PATCH 057/260] Add StreamSplitter to the CHANGELOG. I must have missed this when I first added the class. R=lrn@google.com Review URL: https://codereview.chromium.org//1610873002 . --- pkgs/async/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index b53d7552..af87e243 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -69,6 +69,9 @@ - Added a `StreamGroup` class for merging the events of a group of streams, potentially of unknown size. +- Added a `StreamSplitter` class for splitting a stream into multiple new + streams. + ## 1.1.1 - Updated SDK version constraint to at least 1.9.0. From 0f046288a02bcad9eb2014e8af32323385a55e8f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 25 Jan 2016 14:39:35 -0800 Subject: [PATCH 058/260] Add ClosedStreamSink, and *Completer.setError, and StreamSinkCompleter.fromFuture. R=lrn@google.com Review URL: https://codereview.chromium.org//1615253002 . --- pkgs/async/CHANGELOG.md | 6 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/null_stream_sink.dart | 90 ++++++++++++++ pkgs/async/lib/src/stream_completer.dart | 22 ++-- pkgs/async/lib/src/stream_sink_completer.dart | 29 +++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/null_stream_sink_test.dart | 113 ++++++++++++++++++ pkgs/async/test/stream_completer_test.dart | 28 +++++ .../test/stream_sink_completer_test.dart | 42 +++++++ 9 files changed, 325 insertions(+), 8 deletions(-) create mode 100644 pkgs/async/lib/src/null_stream_sink.dart create mode 100644 pkgs/async/test/null_stream_sink_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index af87e243..15570d70 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,12 @@ - Added `StreamSinkCompleter`, for creating a `StreamSink` now and providing its destination later as another sink. +- Added `StreamCompleter.setError`, a shortcut for emitting a single error event + on the resulting stream. + +- Added `NullStreamSink`, an implementation of `StreamSink` that discards all + events. + ## 1.7.0 - Added `SingleSubscriptionTransformer`, a `StreamTransformer` that converts a diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index ef59b73a..ef1ac04f 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -15,6 +15,7 @@ export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; export "src/future_group.dart"; export "src/lazy_stream.dart"; +export "src/null_stream_sink.dart"; export "src/restartable_timer.dart"; export "src/result_future.dart"; export "src/single_subscription_transformer.dart"; diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart new file mode 100644 index 00000000..aa85924f --- /dev/null +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -0,0 +1,90 @@ +// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +library async.null_stream_sink; + +import 'dart:async'; + +/// A [StreamSink] that discards all events. +/// +/// The sink silently drops events until [close] is called, at which point it +/// throws [StateError]s when events are added. This is the same behavior as a +/// sink whose remote end has closed, such as when a [WebSocket] connection has +/// been closed. +/// +/// This can be used when a sink is needed but no events are actually intended +/// to be added. The [new NullStreamSink.error] constructor can be used to +/// represent errors when creating a sink, since [StreamSink.done] exposes sink +/// errors. For example: +/// +/// ```dart +/// StreamSink> openForWrite(String filename) { +/// try { +/// return new RandomAccessSink(new File(filename).openSync()); +/// } on IOException catch (error, stackTrace) { +/// return new NullStreamSink.error(error, stackTrace); +/// } +/// } +/// ``` +class NullStreamSink implements StreamSink { + final Future done; + + /// Whether the sink has been closed. + var _closed = false; + + /// Whether an [addStream] call is pending. + /// + /// We don't actually add any events from streams, but it does return the + /// [StreamSubscription.cancel] future so to be [StreamSink]-complaint we + /// reject events until that completes. + var _addingStream = false; + + /// Creates a null sink. + /// + /// If [done] is passed, it's used as the [Sink.done] future. Otherwise, a + /// completed future is used. + NullStreamSink({Future done}) : done = done ?? new Future.value(); + + /// Creates a null sink whose [done] future emits [error]. + /// + /// Note that this error will not be considered uncaught. + NullStreamSink.error(error, [StackTrace stackTrace]) + : done = new Future.error(error, stackTrace) + // Don't top-level the error. This gives the user a change to call + // [close] or [done], and matches the behavior of a remote endpoint + // experiencing an error. + ..catchError((_) {}); + + void add(T data) { + _checkEventAllowed(); + } + + void addError(error, [StackTrace stackTrace]) { + _checkEventAllowed(); + } + + Future addStream(Stream stream) { + _checkEventAllowed(); + + _addingStream = true; + var future = stream.listen(null).cancel() ?? new Future.value(); + return future.whenComplete(() { + _addingStream = false; + }); + } + + /// Throws a [StateError] if [close] has been called or an [addStream] call is + /// pending. + void _checkEventAllowed() { + if (_closed) throw new StateError("Cannot add to a closed sink."); + if (_addingStream) { + throw new StateError("Cannot add to a sink while adding a stream."); + } + } + + Future close() { + _closed = true; + return done; + } +} diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index c343e6e7..a6260dcb 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -39,9 +39,7 @@ class StreamCompleter { static Stream fromFuture(Future streamFuture) { var completer = new StreamCompleter(); streamFuture.then(completer.setSourceStream, - onError: (e, s) { - completer.setSourceStream(streamFuture.asStream()); - }); + onError: completer.setError); return completer.stream; } @@ -76,8 +74,8 @@ class StreamCompleter { /// it is immediately listened to, and its events are forwarded to the /// existing subscription. /// - /// Either [setSourceStream] or [setEmpty] may be called at most once. - /// Trying to call either of them again will fail. + /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at + /// most once. Trying to call any of them again will fail. void setSourceStream(Stream sourceStream) { if (_stream._isSourceStreamSet) { throw new StateError("Source stream already set"); @@ -87,14 +85,24 @@ class StreamCompleter { /// Equivalent to setting an empty stream using [setSourceStream]. /// - /// Either [setSourceStream] or [setEmpty] may be called at most once. - /// Trying to call either of them again will fail. + /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at + /// most once. Trying to call any of them again will fail. void setEmpty() { if (_stream._isSourceStreamSet) { throw new StateError("Source stream already set"); } _stream._setEmpty(); } + + /// Completes this to a stream that emits [error] and then closes. + /// + /// This is useful when the process of creating the data for the stream fails. + /// + /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at + /// most once. Trying to call any of them again will fail. + void setError(error, [StackTrace stackTrace]) { + setSourceStream(new Stream.fromFuture(new Future.error(error, stackTrace))); + } } /// Stream completed by [StreamCompleter]. diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index 10caa06b..0b6dc465 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -6,6 +6,8 @@ library async.stream_sink_completer; import 'dart:async'; +import 'null_stream_sink.dart'; + /// A [sink] where the destination is provided later. /// /// The [sink] is a normal sink that you can add events to to immediately, but @@ -29,6 +31,20 @@ class StreamSinkCompleter { /// Returns [sink] typed as a [_CompleterSink]. _CompleterSink get _sink => sink; + /// Convert a `Future` to a `StreamSink`. + /// + /// This creates a sink using a sink completer, and sets the destination sink + /// to the result of the future when the future completes. + /// + /// If the future completes with an error, the returned sink will instead + /// be closed. Its [Sink.done] future will contain the error. + static StreamSink fromFuture(Future sinkFuture) { + var completer = new StreamSinkCompleter(); + sinkFuture.then(completer.setDestinationSink, + onError: completer.setError); + return completer.sink; + } + /// Sets a sink as the destination for events from the [StreamSinkCompleter]'s /// [sink]. /// @@ -41,12 +57,25 @@ class StreamSinkCompleter { /// buffered until the destination is available. /// /// A destination sink may be set at most once. + /// + /// Either of [setDestinationSink] or [setError] may be called at most once. + /// Trying to call either of them again will fail. void setDestinationSink(StreamSink destinationSink) { if (_sink._destinationSink != null) { throw new StateError("Destination sink already set"); } _sink._setDestinationSink(destinationSink); } + + /// Completes this to a closed sink whose [Sink.done] future emits [error]. + /// + /// This is useful when the process of loading the sink fails. + /// + /// Either of [setDestinationSink] or [setError] may be called at most once. + /// Trying to call either of them again will fail. + void setError(error, [StackTrace stackTrace]) { + setDestinationSink(new NullStreamSink.error(error, stackTrace)); + } } /// [StreamSink] completed by [StreamSinkCompleter]. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index ccfa970d..7560836d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.8.0-dev +version: 1.8.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/null_stream_sink_test.dart b/pkgs/async/test/null_stream_sink_test.dart new file mode 100644 index 00000000..244f99c6 --- /dev/null +++ b/pkgs/async/test/null_stream_sink_test.dart @@ -0,0 +1,113 @@ +// Copyright (c) 2016, 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. + +import "dart:async"; + +import "package:async/async.dart"; +import "package:test/test.dart"; + +import "utils.dart"; + +void main() { + group("constructors", () { + test("done defaults to a completed future", () { + var sink = new NullStreamSink(); + expect(sink.done, completes); + }); + + test("a custom future may be passed to done", () async { + var completer = new Completer(); + var sink = new NullStreamSink(done: completer.future); + + var doneFired = false; + sink.done.then((_) { + doneFired = true; + }); + await flushMicrotasks(); + expect(doneFired, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(doneFired, isTrue); + }); + + test("NullStreamSink.error passes an error to done", () { + var sink = new NullStreamSink.error("oh no"); + expect(sink.done, throwsA("oh no")); + }); + }); + + group("events", () { + test("are silently dropped before close", () { + var sink = new NullStreamSink(); + sink.add(1); + sink.addError("oh no"); + }); + + test("throw StateErrors after close", () { + var sink = new NullStreamSink(); + expect(sink.close(), completes); + + expect(() => sink.add(1), throwsStateError); + expect(() => sink.addError("oh no"), throwsStateError); + expect(() => sink.addStream(new Stream.empty()), throwsStateError); + }); + + group("addStream", () { + test("listens to the stream then cancels immediately", () async { + var sink = new NullStreamSink(); + var canceled = false; + var controller = new StreamController(onCancel: () { + canceled = true; + }); + + expect(sink.addStream(controller.stream), completes); + await flushMicrotasks(); + expect(canceled, isTrue); + }); + + test("returns the cancel future", () async { + var completer = new Completer(); + var sink = new NullStreamSink(); + var controller = new StreamController(onCancel: () => completer.future); + + var addStreamFired = false; + sink.addStream(controller.stream).then((_) { + addStreamFired = true; + }); + await flushMicrotasks(); + expect(addStreamFired, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(addStreamFired, isTrue); + }); + + test("pipes errors from the cancel future through addStream", () async { + var sink = new NullStreamSink(); + var controller = new StreamController(onCancel: () => throw "oh no"); + expect(sink.addStream(controller.stream), throwsA("oh no")); + }); + + test("causes events to throw StateErrors until the future completes", + () async { + var sink = new NullStreamSink(); + var future = sink.addStream(new Stream.empty()); + expect(() => sink.add(1), throwsStateError); + expect(() => sink.addError("oh no"), throwsStateError); + expect(() => sink.addStream(new Stream.empty()), throwsStateError); + + await future; + sink.add(1); + sink.addError("oh no"); + expect(sink.addStream(new Stream.empty()), completes); + }); + }); + }); + + test("close returns the done future", () { + var sink = new NullStreamSink.error("oh no"); + expect(sink.close(), throwsA("oh no")); + }); +} diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index cd3ceb9f..76d81d1c 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -337,6 +337,34 @@ main() { }); expect(controller.hasListener, isFalse); }); + + group("setError()", () { + test("produces a stream that emits a single error", () { + var completer = new StreamCompleter(); + completer.stream.listen( + unreachable("data"), + onError: expectAsync((error, stackTrace) { + expect(error, equals("oh no")); + }), + onDone: expectAsync(() {})); + + completer.setError("oh no"); + }); + + test("produces a stream that emits a single error on a later listen", + () async { + var completer = new StreamCompleter(); + completer.setError("oh no"); + await flushMicrotasks(); + + completer.stream.listen( + unreachable("data"), + onError: expectAsync((error, stackTrace) { + expect(error, equals("oh no")); + }), + onDone: expectAsync(() {})); + }); + }); } Stream createStream() async* { diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 3892d267..82393fcd 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -245,6 +245,48 @@ main() { expect(completer.sink.close(), completes); }); + group("fromFuture()", () { + test("with a successful completion", () async { + var futureCompleter = new Completer(); + var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); + sink.add(1); + sink.add(2); + sink.add(3); + sink.close(); + + var testSink = new TestSink(); + futureCompleter.complete(testSink); + await testSink.done; + + expect(testSink.results[0].asValue.value, equals(1)); + expect(testSink.results[1].asValue.value, equals(2)); + expect(testSink.results[2].asValue.value, equals(3)); + }); + + test("with an error", () async { + var futureCompleter = new Completer(); + var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); + expect(sink.done, throwsA("oh no")); + futureCompleter.completeError("oh no"); + }); + }); + + group("setError()", () { + test("produces a closed sink with the error", () { + completer.setError("oh no"); + expect(completer.sink.done, throwsA("oh no")); + expect(completer.sink.close(), throwsA("oh no")); + }); + + test("produces an error even if done was accessed earlier", () async { + expect(completer.sink.done, throwsA("oh no")); + expect(completer.sink.close(), throwsA("oh no")); + await flushMicrotasks(); + + completer.setError("oh no"); + }); + }); + test("doesn't allow the destination sink to be set multiple times", () { completer.setDestinationSink(new TestSink()); expect(() => completer.setDestinationSink(new TestSink()), From d7a9dc8ccc11832d1d2f9a09004e245aa1655b81 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 7 Mar 2016 13:27:08 -0800 Subject: [PATCH 059/260] Remove unused imports --- pkgs/async/test/async_memoizer_test.dart | 2 -- pkgs/async/test/restartable_timer_test.dart | 2 -- 2 files changed, 4 deletions(-) diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart index b8ca38a1..dc008f1e 100644 --- a/pkgs/async/test/async_memoizer_test.dart +++ b/pkgs/async/test/async_memoizer_test.dart @@ -2,8 +2,6 @@ // 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. -import 'dart:async'; - import 'package:async/async.dart'; import 'package:test/test.dart'; diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index 6732b813..7dbbc2e4 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -2,8 +2,6 @@ // 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. -import 'dart:async'; - import 'package:async/async.dart'; import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; From 60e3c881462257626796e8099feb74b13cc36b03 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 8 Mar 2016 12:24:15 -0800 Subject: [PATCH 060/260] Modernize the package's style. This moves the package to new-style doc comments, deprecates separate top-level libraries, and removes library tags. It also deprecates some top-level classes in favor of static const fields. There's more that could be done, but this fixes most of the low-hanging fruit. R=lrn@google.com Review URL: https://codereview.chromium.org//1777453002 . --- pkgs/async/CHANGELOG.md | 13 + pkgs/async/README.md | 31 +-- pkgs/async/lib/async.dart | 12 +- pkgs/async/lib/result.dart | 257 +----------------- pkgs/async/lib/src/async_memoizer.dart | 6 +- pkgs/async/lib/src/cancelable_operation.dart | 2 - pkgs/async/lib/src/delegate/event_sink.dart | 2 - pkgs/async/lib/src/delegate/future.dart | 2 - pkgs/async/lib/src/delegate/sink.dart | 2 - .../lib/src/delegate/stream_consumer.dart | 2 - pkgs/async/lib/src/delegate/stream_sink.dart | 2 - .../lib/src/delegate/stream_subscription.dart | 2 - pkgs/async/lib/src/future_group.dart | 4 +- pkgs/async/lib/src/lazy_stream.dart | 2 - pkgs/async/lib/src/null_stream_sink.dart | 2 - pkgs/async/lib/src/restartable_timer.dart | 2 - pkgs/async/lib/src/result.dart | 164 +++++++++++ pkgs/async/lib/src/result/capture_sink.dart | 27 ++ .../lib/src/result/capture_transformer.dart | 22 ++ pkgs/async/lib/src/result/error.dart | 45 +++ .../future.dart} | 4 +- pkgs/async/lib/src/result/release_sink.dart | 34 +++ .../lib/src/result/release_transformer.dart | 22 ++ pkgs/async/lib/src/result/value.dart | 30 ++ .../src/single_subscription_transformer.dart | 2 - pkgs/async/lib/src/stream_completer.dart | 2 - pkgs/async/lib/src/stream_group.dart | 2 - pkgs/async/lib/src/stream_queue.dart | 4 +- pkgs/async/lib/src/stream_sink_completer.dart | 2 - .../lib/src/stream_sink_transformer.dart | 2 - .../handler_transformer.dart | 2 - .../stream_transformer_wrapper.dart | 2 - pkgs/async/lib/src/stream_splitter.dart | 4 +- pkgs/async/lib/src/stream_zip.dart | 120 ++++++++ pkgs/async/lib/src/subscription_stream.dart | 2 - pkgs/async/lib/stream_zip.dart | 116 +------- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/result_test.dart | 2 +- pkgs/async/test/stream_group_test.dart | 2 - pkgs/async/test/stream_zip_test.dart | 3 +- pkgs/async/test/utils.dart | 2 - 41 files changed, 513 insertions(+), 449 deletions(-) create mode 100644 pkgs/async/lib/src/result.dart create mode 100644 pkgs/async/lib/src/result/capture_sink.dart create mode 100644 pkgs/async/lib/src/result/capture_transformer.dart create mode 100644 pkgs/async/lib/src/result/error.dart rename pkgs/async/lib/src/{result_future.dart => result/future.dart} (94%) create mode 100644 pkgs/async/lib/src/result/release_sink.dart create mode 100644 pkgs/async/lib/src/result/release_transformer.dart create mode 100644 pkgs/async/lib/src/result/value.dart create mode 100644 pkgs/async/lib/src/stream_zip.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 15570d70..946d3a8f 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,16 @@ +## 1.9.0 + +* Deprecate top-level libraries other than `package:async/async.dart`, which + exports these libraries' interfaces. + +* Add `Result.captureStreamTransformer`, `Result.releaseStreamTransformer`, + `Result.captureSinkTransformer`, and `Result.releaseSinkTransformer`. + +* Deprecate `CaptureStreamTransformer`, `ReleaseStreamTransformer`, + `CaptureSink`, and `ReleaseSink`. `Result.captureStreamTransformer`, + `Result.releaseStreamTransformer`, `Result.captureSinkTransformer`, and + `Result.releaseSinkTransformer` should be used instead. + ## 1.8.0 - Added `StreamSinkCompleter`, for creating a `StreamSink` now and providing its diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 3820f064..16cd55f5 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -1,28 +1,15 @@ -# Async utilities package - -Contains tools to work with asynchronous computations. - -The package contains `Stream` and `Future` related functionality, -as well as sub-libraries with different utilities. +Contains utility classes in the style of `dart:async` to work with asynchronous +computations. ### Zipping streams -The "stream_zip.dart" sub-library contains functionality -to combine several streams of events into a single stream of tuples of events. +The `StreamZip` class can combine several streams of events into a single stream +of tuples of events. ### Results -The "result.dart" sub-library introduces a `Result` class that can hold either -a value or an error. -It allows capturing an asynchronous computation which can give either a value -or an error, into an asynchronous computation that always gives a `Result` -value, where errors can be treated as data. -It also allows releasing the `Result` back into an asynchronous computation. - -### History. -This package is unrelated to the discontinued `async` package with version 0.1.7. - -## Features and bugs - -Please file feature requests and bugs at the [issue tracker][tracker]. -[tracker]: https://github.com/dart-lang/async/issues +The package introduces a `Result` class that can hold either a value or an +error. It allows capturing an asynchronous computation which can give either a +value or an error, into an asynchronous computation that always gives a `Result` +value, where errors can be treated as data. It also allows releasing the +`Result` back into an asynchronous computation. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index ef1ac04f..0d3e051c 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -2,9 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library dart.pkg.async; - -export "result.dart"; export "src/async_memoizer.dart"; export "src/cancelable_operation.dart"; export "src/delegate/event_sink.dart"; @@ -17,7 +14,12 @@ export "src/future_group.dart"; export "src/lazy_stream.dart"; export "src/null_stream_sink.dart"; export "src/restartable_timer.dart"; -export "src/result_future.dart"; +export "src/result.dart"; +export "src/result/capture_transformer.dart"; +export "src/result/error.dart"; +export "src/result/future.dart"; +export "src/result/release_transformer.dart"; +export "src/result/value.dart"; export "src/single_subscription_transformer.dart"; export "src/stream_completer.dart"; export "src/stream_group.dart"; @@ -25,5 +27,5 @@ export "src/stream_queue.dart"; export "src/stream_sink_completer.dart"; export "src/stream_sink_transformer.dart"; export "src/stream_splitter.dart"; +export "src/stream_zip.dart"; export "src/subscription_stream.dart"; -export "stream_zip.dart"; diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart index 13e3dd94..627f2008 100644 --- a/pkgs/async/lib/result.dart +++ b/pkgs/async/lib/result.dart @@ -2,255 +2,12 @@ // 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. -/// Capture asynchronous results into synchronous values. -/// -/// Capturing a result (either a returned value or a thrown error) -/// means converting it into a [Result] - -/// either a [ValueResult] or an [ErrorResult]. -/// -/// This value can release itself by writing itself either to a -/// [EventSink] or a [Completer], or by becoming a [Future]. +/// Import `async.dart` instead. +@Deprecated("Will be removed in async 2.0.0.") library dart.pkg.async.results; -import "dart:async"; - -/// The result of a computation. -abstract class Result { - /// Create a `Result` with the result of calling [computation]. - /// - /// This generates either a [ValueResult] with the value returned by - /// calling `computation`, or an [ErrorResult] with an error thrown by - /// the call. - factory Result(T computation()) { - try { - return new ValueResult(computation()); - } catch (e, s) { - return new ErrorResult(e, s); - } - } - - /// Create a `Result` holding a value. - /// - /// Alias for [ValueResult.ValueResult]. - factory Result.value(T value) = ValueResult; - - /// Create a `Result` holding an error. - /// - /// Alias for [ErrorResult.ErrorResult]. - factory Result.error(Object error, [StackTrace stackTrace]) => - new ErrorResult(error, stackTrace); - - // Helper functions. - static _captureValue(value) => new ValueResult(value); - static _captureError(error, stack) => new ErrorResult(error, stack); - static _release(Result v) { - if (v.isValue) return v.asValue.value; // Avoid wrapping in future. - return v.asFuture; - } - - /// Capture the result of a future into a `Result` future. - /// - /// The resulting future will never have an error. - /// Errors have been converted to an [ErrorResult] value. - static Future capture(Future future) { - return future.then(_captureValue, onError: _captureError); - } - - /// Release the result of a captured future. - /// - /// Converts the [Result] value of the given [future] to a value or error - /// completion of the returned future. - /// - /// If [future] completes with an error, the returned future completes with - /// the same error. - static Future release(Future future) { - return future.then(_release); - } - - /// Capture the results of a stream into a stream of [Result] values. - /// - /// The returned stream will not have any error events. - /// Errors from the source stream have been converted to [ErrorResult]s. - /// - /// Shorthand for transforming the stream using [CaptureStreamTransformer]. - static Stream captureStream(Stream source) { - return source.transform(const CaptureStreamTransformer()); - } - - /// Release a stream of [result] values into a stream of the results. - /// - /// `Result` values of the source stream become value or error events in - /// the returned stream as appropriate. - /// Errors from the source stream become errors in the returned stream. - /// - /// Shorthand for transforming the stream using [ReleaseStreamTransformer]. - static Stream releaseStream(Stream source) { - return source.transform(const ReleaseStreamTransformer()); - } - - /// Converts a result of a result to a single result. - /// - /// If the result is an error, or it is a `Result` value - /// which is then an error, then a result with that error is returned. - /// Otherwise both levels of results are value results, and a single - /// result with the value is returned. - static Result flatten(Result result) { - if (result.isError) return result; - return result.asValue.value; - } - - /// Whether this result is a value result. - /// - /// Always the opposite of [isError]. - bool get isValue; - - /// Whether this result is an error result. - /// - /// Always the opposite of [isValue]. - bool get isError; - - /// If this is a value result, return itself. - /// - /// Otherwise return `null`. - ValueResult get asValue; - - /// If this is an error result, return itself. - /// - /// Otherwise return `null`. - ErrorResult get asError; - - /// Complete a completer with this result. - void complete(Completer completer); - - /// Add this result to an [EventSink]. - /// - /// Calls the sink's `add` or `addError` method as appropriate. - void addTo(EventSink sink); - - /// Creates a future completed with this result as a value or an error. - Future get asFuture; -} - -/// A result representing a returned value. -class ValueResult implements Result { - final T value; - ValueResult(this.value); - bool get isValue => true; - bool get isError => false; - ValueResult get asValue => this; - ErrorResult get asError => null; - void complete(Completer completer) { - completer.complete(value); - } - void addTo(EventSink sink) { - sink.add(value); - } - Future get asFuture => new Future.value(value); -} - -/// A result representing a thrown error. -class ErrorResult implements Result { - final error; - final StackTrace stackTrace; - ErrorResult(this.error, this.stackTrace); - bool get isValue => false; - bool get isError => true; - ValueResult get asValue => null; - ErrorResult get asError => this; - void complete(Completer completer) { - completer.completeError(error, stackTrace); - } - void addTo(EventSink sink) { - sink.addError(error, stackTrace); - } - Future get asFuture => new Future.error(error, stackTrace); - - /// Calls an error handler with the error and stacktrace. - /// - /// An async error handler function is either a function expecting two - /// arguments, which will be called with the error and the stack trace, - /// or it has to be a function expecting only one argument, - /// which will be called with only the error. - void handle(Function errorHandler) { - if (errorHandler is ZoneBinaryCallback) { - errorHandler(error, stackTrace); - } else { - errorHandler(error); - } - } -} - -/// A stream transformer that captures a stream of events into [Result]s. -/// -/// The result of the transformation is a stream of [Result] values and -/// no error events. -class CaptureStreamTransformer implements StreamTransformer> { - const CaptureStreamTransformer(); - - Stream> bind(Stream source) { - return new Stream>.eventTransformed(source, _createSink); - } - - static EventSink _createSink(EventSink sink) { - return new CaptureSink(sink); - } -} - -/// A stream transformer that releases a stream of result events. -/// -/// The result of the transformation is a stream of values and -/// error events. -class ReleaseStreamTransformer implements StreamTransformer, T> { - const ReleaseStreamTransformer(); - - Stream bind(Stream> source) { - return new Stream.eventTransformed(source, _createSink); - } - - static EventSink _createSink(EventSink sink) { - return new ReleaseSink(sink); - } -} - -/// An event sink wrapper that captures the incoming events. -/// -/// Wraps an [EventSink] that expects [Result] values. -/// Accepts any value and error result, -/// and passes them to the wrapped sink as [Result] values. -/// -/// The wrapped sink will never receive an error event. -class CaptureSink implements EventSink { - final EventSink _sink; - - CaptureSink(EventSink> sink) : _sink = sink; - void add(T value) { _sink.add(new ValueResult(value)); } - void addError(Object error, [StackTrace stackTrace]) { - _sink.add(new ErrorResult(error, stackTrace)); - } - void close() { _sink.close(); } -} - -/// An event sink wrapper that releases the incoming result events. -/// -/// Wraps an output [EventSink] that expects any result. -/// Accepts [Result] values, and puts the result value or error into the -/// corresponding output sink add method. -class ReleaseSink implements EventSink> { - final EventSink _sink; - ReleaseSink(EventSink sink) : _sink = sink; - void add(Result result) { - if (result.isValue) { - _sink.add(result.asValue.value); - } else { - ErrorResult error = result.asError; - _sink.addError(error.error, error.stackTrace); - } - } - void addError(Object error, [StackTrace stackTrace]) { - // Errors may be added by intermediate processing, even if it is never - // added by CaptureSink. - _sink.addError(error, stackTrace); - } - - void close() { _sink.close(); } -} +export "src/result.dart"; +export "src/result/capture_transformer.dart"; +export "src/result/error.dart"; +export "src/result/release_transformer.dart"; +export "src/result/value.dart"; diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index 41c3dae8..3612a7d0 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.async_memoizer; - import 'dart:async'; /// A class for running an asynchronous function exactly once and caching its @@ -35,12 +33,12 @@ class AsyncMemoizer { Future get future => _completer.future; final _completer = new Completer(); - /// Whether [run] has been called yet. + /// Whether [runOnce] has been called yet. bool get hasRun => _completer.isCompleted; /// Runs the function, [computation], if it hasn't been run before. /// - /// If [run] has already been called, this returns the original result. + /// If [runOnce] has already been called, this returns the original result. Future runOnce(computation()) { if (!hasRun) _completer.complete(new Future.sync(computation)); return future; diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index a48c94fe..1ba6b3c4 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.cancelable_operation; - import 'dart:async'; import 'package:async/async.dart'; diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 337e7a81..5a525dfc 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegate.event_sink; - import 'dart:async'; /// Simple delegating wrapper around an [EventSink]. diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 34f61589..5e84e4f6 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegate.future; - import 'dart:async'; /// A wrapper that forwards calls to a [Future]. diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index bb50da33..cee29375 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegate.sink; - /// Simple delegating wrapper around a [Sink]. /// /// Subclasses can override individual methods, or use this to expose only the diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index 8162db6d..dcaf0c25 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegate.stream_consumer; - import 'dart:async'; /// Simple delegating wrapper around a [StreamConsumer]. diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index b6ace650..e06afc1b 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegate.stream_sink; - import 'dart:async'; /// Simple delegating wrapper around a [StreamSink]. diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index ff9b6658..e7153b2a 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.delegate.stream_subscription; - import 'dart:async'; /// Simple delegating wrapper around a [StreamSubscription]. diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 02ff185c..114c1334 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.future_group; - import 'dart:async'; /// A collection of futures waits until all added [Future]s complete. @@ -35,7 +33,7 @@ class FutureGroup implements Sink> { Future> get future => _completer.future; final _completer = new Completer>(); - /// Whether this group is waiting on any futures. + /// Whether this group has no pending futures. bool get isIdle => _pending == 0; /// A broadcast stream that emits a `null` event whenever the last pending diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index 1878d4cf..3ba49531 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.lazy_stream; - import "dart:async"; import "stream_completer.dart"; diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart index aa85924f..c83790ca 100644 --- a/pkgs/async/lib/src/null_stream_sink.dart +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.null_stream_sink; - import 'dart:async'; /// A [StreamSink] that discards all events. diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart index 05196d28..eed51e6d 100644 --- a/pkgs/async/lib/src/restartable_timer.dart +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.restartable_timer; - import 'dart:async'; /// A non-periodic timer that can be restarted any number of times. diff --git a/pkgs/async/lib/src/result.dart b/pkgs/async/lib/src/result.dart new file mode 100644 index 00000000..482cf9ba --- /dev/null +++ b/pkgs/async/lib/src/result.dart @@ -0,0 +1,164 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'result/capture_transformer.dart'; +import 'result/error.dart'; +import 'result/release_transformer.dart'; +import 'result/value.dart'; +import 'stream_sink_transformer.dart'; + +/// The result of a computation. +/// +/// Capturing a result (either a returned value or a thrown error) means +/// converting it into a [Result] - either a [ValueResult] or an [ErrorResult]. +/// +/// This value can release itself by writing itself either to a [EventSink] or a +/// [Completer], or by becoming a [Future]. +abstract class Result { + /// A stream transformer that captures a stream of events into [Result]s. + /// + /// The result of the transformation is a stream of [Result] values and no + /// error events. This is the transformer used by [captureStream]. + static const StreamTransformer captureStreamTransformer = + const CaptureStreamTransformer(); + + /// A stream transformer that releases a stream of result events. + /// + /// The result of the transformation is a stream of values and error events. + /// This is the transformer used by [releaseStream]. + static const StreamTransformer releaseStreamTransformer = + const ReleaseStreamTransformer(); + + /// A sink transformer that captures events into [Result]s. + /// + /// The result of the transformation is a sink that only forwards [Result] + /// values and no error events. + static const StreamSinkTransformer captureSinkTransformer = + const StreamSinkTransformer.fromStreamTransformer( + const CaptureStreamTransformer()); + + /// A sink transformer that releases result events. + /// + /// The result of the transformation is a sink that forwards of values and + /// error events. + static const StreamSinkTransformer releaseSinkTransformer = + const StreamSinkTransformer.fromStreamTransformer( + const ReleaseStreamTransformer()); + + /// Create a `Result` with the result of calling [computation]. + /// + /// This generates either a [ValueResult] with the value returned by + /// calling `computation`, or an [ErrorResult] with an error thrown by + /// the call. + factory Result(T computation()) { + try { + return new ValueResult(computation()); + } catch (e, s) { + return new ErrorResult(e, s); + } + } + + /// Create a `Result` holding a value. + /// + /// Alias for [ValueResult.ValueResult]. + factory Result.value(T value) = ValueResult; + + /// Create a `Result` holding an error. + /// + /// Alias for [ErrorResult.ErrorResult]. + factory Result.error(Object error, [StackTrace stackTrace]) => + new ErrorResult(error, stackTrace); + + // Helper functions. + static _captureValue(value) => new ValueResult(value); + static _captureError(error, stack) => new ErrorResult(error, stack); + static _release(Result v) { + if (v.isValue) return v.asValue.value; // Avoid wrapping in future. + return v.asFuture; + } + + /// Capture the result of a future into a `Result` future. + /// + /// The resulting future will never have an error. + /// Errors have been converted to an [ErrorResult] value. + static Future capture(Future future) { + return future.then(_captureValue, onError: _captureError); + } + + /// Release the result of a captured future. + /// + /// Converts the [Result] value of the given [future] to a value or error + /// completion of the returned future. + /// + /// If [future] completes with an error, the returned future completes with + /// the same error. + static Future release(Future future) { + return future.then(_release); + } + + /// Capture the results of a stream into a stream of [Result] values. + /// + /// The returned stream will not have any error events. + /// Errors from the source stream have been converted to [ErrorResult]s. + /// + /// Shorthand for transforming the stream using [captureStreamTransformer]. + static Stream captureStream(Stream source) { + return source.transform(captureStreamTransformer); + } + + /// Release a stream of [result] values into a stream of the results. + /// + /// `Result` values of the source stream become value or error events in + /// the returned stream as appropriate. + /// Errors from the source stream become errors in the returned stream. + /// + /// Shorthand for transforming the stream using [releaseStreamTransformer]. + static Stream releaseStream(Stream source) { + return source.transform(releaseStreamTransformer); + } + + /// Converts a result of a result to a single result. + /// + /// If the result is an error, or it is a `Result` value + /// which is then an error, then a result with that error is returned. + /// Otherwise both levels of results are value results, and a single + /// result with the value is returned. + static Result flatten(Result result) { + if (result.isError) return result; + return result.asValue.value; + } + + /// Whether this result is a value result. + /// + /// Always the opposite of [isError]. + bool get isValue; + + /// Whether this result is an error result. + /// + /// Always the opposite of [isValue]. + bool get isError; + + /// If this is a value result, return itself. + /// + /// Otherwise return `null`. + ValueResult get asValue; + + /// If this is an error result, return itself. + /// + /// Otherwise return `null`. + ErrorResult get asError; + + /// Complete a completer with this result. + void complete(Completer completer); + + /// Add this result to an [EventSink]. + /// + /// Calls the sink's `add` or `addError` method as appropriate. + void addTo(EventSink sink); + + /// Creates a future completed with this result as a value or an error. + Future get asFuture; +} diff --git a/pkgs/async/lib/src/result/capture_sink.dart b/pkgs/async/lib/src/result/capture_sink.dart new file mode 100644 index 00000000..74745251 --- /dev/null +++ b/pkgs/async/lib/src/result/capture_sink.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../result.dart'; + +/// Use [Result.captureSinkTransformer]. +@Deprecated("Will be removed in async 2.0.0.") +class CaptureSink implements EventSink { + final EventSink _sink; + + CaptureSink(EventSink> sink) : _sink = sink; + + void add(T value) { + _sink.add(new Result.value(value)); + } + + void addError(Object error, [StackTrace stackTrace]) { + _sink.add(new Result.error(error, stackTrace)); + } + + void close() { + _sink.close(); + } +} diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart new file mode 100644 index 00000000..ca4d0d39 --- /dev/null +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../result.dart'; +import 'capture_sink.dart'; + +/// Use [Result.captureTransformer] instead. +@Deprecated("Will be removed in async 2.0.0.") +class CaptureStreamTransformer implements StreamTransformer> { + const CaptureStreamTransformer(); + + Stream> bind(Stream source) { + return new Stream>.eventTransformed(source, _createSink); + } + + static EventSink _createSink(EventSink sink) { + return new CaptureSink(sink); + } +} diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart new file mode 100644 index 00000000..eecf68e4 --- /dev/null +++ b/pkgs/async/lib/src/result/error.dart @@ -0,0 +1,45 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../result.dart'; +import 'value.dart'; + +/// A result representing a thrown error. +class ErrorResult implements Result { + final error; + final StackTrace stackTrace; + + bool get isValue => false; + bool get isError => true; + ValueResult get asValue => null; + ErrorResult get asError => this; + + ErrorResult(this.error, this.stackTrace); + + void complete(Completer completer) { + completer.completeError(error, stackTrace); + } + + void addTo(EventSink sink) { + sink.addError(error, stackTrace); + } + + Future get asFuture => new Future.error(error, stackTrace); + + /// Calls an error handler with the error and stacktrace. + /// + /// An async error handler function is either a function expecting two + /// arguments, which will be called with the error and the stack trace, or it + /// has to be a function expecting only one argument, which will be called + /// with only the error. + void handle(Function errorHandler) { + if (errorHandler is ZoneBinaryCallback) { + errorHandler(error, stackTrace); + } else { + errorHandler(error); + } + } +} diff --git a/pkgs/async/lib/src/result_future.dart b/pkgs/async/lib/src/result/future.dart similarity index 94% rename from pkgs/async/lib/src/result_future.dart rename to pkgs/async/lib/src/result/future.dart index 311e83fe..db9dd822 100644 --- a/pkgs/async/lib/src/result_future.dart +++ b/pkgs/async/lib/src/result/future.dart @@ -2,12 +2,10 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.result_future; - import 'dart:async'; +import '../delegate/future.dart'; import '../result.dart'; -import 'delegate/future.dart'; /// A [Future] wrapper that provides synchronous access to the result of the /// wrapped [Future] once it's completed. diff --git a/pkgs/async/lib/src/result/release_sink.dart b/pkgs/async/lib/src/result/release_sink.dart new file mode 100644 index 00000000..114981b6 --- /dev/null +++ b/pkgs/async/lib/src/result/release_sink.dart @@ -0,0 +1,34 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../result.dart'; + +/// Use [Result.captureSinkTransformer]. +@Deprecated("Will be removed in async 2.0.0.") +class ReleaseSink implements EventSink> { + final EventSink _sink; + + ReleaseSink(EventSink sink) : _sink = sink; + + void add(Result result) { + if (result.isValue) { + _sink.add(result.asValue.value); + } else { + var error = result.asError; + _sink.addError(error.error, error.stackTrace); + } + } + + void addError(Object error, [StackTrace stackTrace]) { + // Errors may be added by intermediate processing, even if it is never + // added by CaptureSink. + _sink.addError(error, stackTrace); + } + + void close() { + _sink.close(); + } +} diff --git a/pkgs/async/lib/src/result/release_transformer.dart b/pkgs/async/lib/src/result/release_transformer.dart new file mode 100644 index 00000000..456ed0a9 --- /dev/null +++ b/pkgs/async/lib/src/result/release_transformer.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../result.dart'; +import 'release_sink.dart'; + +/// Use [Result.releaseTransformer] instead. +@Deprecated("Will be removed in async 2.0.0.") +class ReleaseStreamTransformer implements StreamTransformer, T> { + const ReleaseStreamTransformer(); + + Stream bind(Stream> source) { + return new Stream.eventTransformed(source, _createSink); + } + + static EventSink _createSink(EventSink sink) { + return new ReleaseSink(sink); + } +} diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart new file mode 100644 index 00000000..f065ab67 --- /dev/null +++ b/pkgs/async/lib/src/result/value.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../result.dart'; +import 'error.dart'; + +/// A result representing a returned value. +class ValueResult implements Result { + final T value; + + bool get isValue => true; + bool get isError => false; + ValueResult get asValue => this; + ErrorResult get asError => null; + + ValueResult(this.value); + + void complete(Completer completer) { + completer.complete(value); + } + + void addTo(EventSink sink) { + sink.add(value); + } + + Future get asFuture => new Future.value(value); +} diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index 28fd512d..4255175f 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.single_subscription_transformer; - import 'dart:async'; /// A transformer that converts a broadcast stream into a single-subscription diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index a6260dcb..73557b2a 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_completer; - import "dart:async"; /// A single-subscription [stream] where the contents are provided later. diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index d99f5151..6239fc23 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_group; - import 'dart:async'; /// A collection of streams whose events are unified and sent through a central diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 09b3a75b..8482e0c6 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -2,14 +2,12 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_events; - import 'dart:async'; import 'dart:collection'; +import "result.dart"; import "subscription_stream.dart"; import "stream_completer.dart"; -import "../result.dart"; /// An asynchronous pull-based interface for accessing stream events. /// diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index 0b6dc465..b53a8d23 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_sink_completer; - import 'dart:async'; import 'null_stream_sink.dart'; diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index a8410ab2..6cde04a7 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_sink_transformer; - import 'dart:async'; import 'stream_sink_transformer/handler_transformer.dart'; diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index e5d8e3c6..a20a3fa1 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_sink_transformer.handler_transformer; - import 'dart:async'; import '../stream_sink_transformer.dart'; diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart index b53f2086..83d2b197 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_sink_transformer.stream_transformer_wrapper; - import 'dart:async'; import '../stream_sink_transformer.dart'; diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index ac401a7b..ec648aad 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -2,12 +2,10 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.stream_splitter; - import 'dart:async'; -import '../result.dart'; import 'future_group.dart'; +import 'result.dart'; /// A class that splits a single source stream into an arbitrary number of /// (single-subscription) streams (called "branch") that emit the same events. diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart new file mode 100644 index 00000000..432cc22c --- /dev/null +++ b/pkgs/async/lib/src/stream_zip.dart @@ -0,0 +1,120 @@ +// Copyright (c) 2016, 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. + +import "dart:async"; + +/// A stream that combines the values of other streams. +/// +/// This emits lists of collected values from each input stream. The first list +/// contains the first value emitted by each stream, the second contrains the +/// second value, and so on. The lists have the same ordering as the iterable +/// passed to [new StreamZip]. +/// +/// Any errors from any of the streams are forwarded directly to this stream. +class StreamZip extends Stream> { + final Iterable> _streams; + + StreamZip(Iterable> streams) : _streams = streams; + + StreamSubscription> listen(void onData(List data), { + Function onError, + void onDone(), + bool cancelOnError}) { + cancelOnError = identical(true, cancelOnError); + List subscriptions = []; + StreamController controller; + List current; + int dataCount = 0; + + /// Called for each data from a subscription in [subscriptions]. + void handleData(int index, data) { + current[index] = data; + dataCount++; + if (dataCount == subscriptions.length) { + List data = current; + current = new List(subscriptions.length); + dataCount = 0; + for (int i = 0; i < subscriptions.length; i++) { + if (i != index) subscriptions[i].resume(); + } + controller.add(data); + } else { + subscriptions[index].pause(); + } + } + + /// Called for each error from a subscription in [subscriptions]. + /// Except if [cancelOnError] is true, in which case the function below + /// is used instead. + void handleError(Object error, StackTrace stackTrace) { + controller.addError(error, stackTrace); + } + + /// Called when a subscription has an error and [cancelOnError] is true. + /// + /// Prematurely cancels all subscriptions since we know that we won't + /// be needing any more values. + void handleErrorCancel(Object error, StackTrace stackTrace) { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].cancel(); + } + controller.addError(error, stackTrace); + } + + void handleDone() { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].cancel(); + } + controller.close(); + } + + try { + for (Stream stream in _streams) { + int index = subscriptions.length; + subscriptions.add(stream.listen( + (data) { handleData(index, data); }, + onError: cancelOnError ? handleError : handleErrorCancel, + onDone: handleDone, + cancelOnError: cancelOnError)); + } + } catch (e) { + for (int i = subscriptions.length - 1; i >= 0; i--) { + subscriptions[i].cancel(); + } + rethrow; + } + + current = new List(subscriptions.length); + + controller = new StreamController( + onPause: () { + for (int i = 0; i < subscriptions.length; i++) { + // This may pause some subscriptions more than once. + // These will not be resumed by onResume below, but must wait for the + // next round. + subscriptions[i].pause(); + } + }, + onResume: () { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].resume(); + } + }, + onCancel: () { + for (int i = 0; i < subscriptions.length; i++) { + // Canceling more than once is safe. + subscriptions[i].cancel(); + } + } + ); + + if (subscriptions.isEmpty) { + controller.close(); + } + return controller.stream.listen(onData, + onError: onError, + onDone: onDone, + cancelOnError: cancelOnError); + } +} diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index bdf329b9..4f58c76f 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.subscription_stream; - import 'dart:async'; import "delegate/stream_subscription.dart"; diff --git a/pkgs/async/lib/stream_zip.dart b/pkgs/async/lib/stream_zip.dart index 055489db..cdeb5c97 100644 --- a/pkgs/async/lib/stream_zip.dart +++ b/pkgs/async/lib/stream_zip.dart @@ -2,118 +2,8 @@ // 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. -/** - * Help for combining multiple streams into a single stream. - */ +/// Import `async.dart` instead. +@Deprecated("Will be removed in async 2.0.0.") library dart.pkg.async.stream_zip; -import "dart:async"; - -/** - * A stream that combines the values of other streams. - */ -class StreamZip extends Stream { - final Iterable _streams; - StreamZip(Iterable streams) : _streams = streams; - - StreamSubscription listen(void onData(List data), { - Function onError, - void onDone(), - bool cancelOnError}) { - cancelOnError = identical(true, cancelOnError); - List subscriptions = []; - StreamController controller; - List current; - int dataCount = 0; - - /// Called for each data from a subscription in [subscriptions]. - void handleData(int index, data) { - current[index] = data; - dataCount++; - if (dataCount == subscriptions.length) { - List data = current; - current = new List(subscriptions.length); - dataCount = 0; - for (int i = 0; i < subscriptions.length; i++) { - if (i != index) subscriptions[i].resume(); - } - controller.add(data); - } else { - subscriptions[index].pause(); - } - } - - /// Called for each error from a subscription in [subscriptions]. - /// Except if [cancelOnError] is true, in which case the function below - /// is used instead. - void handleError(Object error, StackTrace stackTrace) { - controller.addError(error, stackTrace); - } - - /// Called when a subscription has an error and [cancelOnError] is true. - /// - /// Prematurely cancels all subscriptions since we know that we won't - /// be needing any more values. - void handleErrorCancel(Object error, StackTrace stackTrace) { - for (int i = 0; i < subscriptions.length; i++) { - subscriptions[i].cancel(); - } - controller.addError(error, stackTrace); - } - - void handleDone() { - for (int i = 0; i < subscriptions.length; i++) { - subscriptions[i].cancel(); - } - controller.close(); - } - - try { - for (Stream stream in _streams) { - int index = subscriptions.length; - subscriptions.add(stream.listen( - (data) { handleData(index, data); }, - onError: cancelOnError ? handleError : handleErrorCancel, - onDone: handleDone, - cancelOnError: cancelOnError)); - } - } catch (e) { - for (int i = subscriptions.length - 1; i >= 0; i--) { - subscriptions[i].cancel(); - } - rethrow; - } - - current = new List(subscriptions.length); - - controller = new StreamController( - onPause: () { - for (int i = 0; i < subscriptions.length; i++) { - // This may pause some subscriptions more than once. - // These will not be resumed by onResume below, but must wait for the - // next round. - subscriptions[i].pause(); - } - }, - onResume: () { - for (int i = 0; i < subscriptions.length; i++) { - subscriptions[i].resume(); - } - }, - onCancel: () { - for (int i = 0; i < subscriptions.length; i++) { - // Canceling more than once is safe. - subscriptions[i].cancel(); - } - } - ); - - if (subscriptions.isEmpty) { - controller.close(); - } - return controller.stream.listen(onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError); - } -} +export "src/stream_zip.dart"; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 7560836d..2430d752 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.8.0 +version: 1.9.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 848c4552..7af3fcf3 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -5,7 +5,7 @@ import "dart:async"; import "dart:collection"; -import "package:async/result.dart"; +import "package:async/async.dart"; import "package:stack_trace/stack_trace.dart"; import "package:test/test.dart"; diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index db6c9387..5078dad4 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -2,8 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -library async.test.stream_group_test; - import 'dart:async'; import 'package:async/async.dart'; diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 35ace7d1..47f3d8dc 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -3,7 +3,8 @@ // BSD-style license that can be found in the LICENSE file. import "dart:async"; -import "package:async/stream_zip.dart"; + +import "package:async/async.dart"; import "package:test/test.dart"; /// Create an error with the same values as [base], except that it throwsA diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 445b9fcf..c32ea2c1 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. /// Helper utilities for testing. -library async.test.util; - import "dart:async"; import "package:async/async.dart"; From 8bddb85c0eb46a1fb31663e7293c3aa7b2ca0621 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 29 Mar 2016 17:57:24 -0700 Subject: [PATCH 061/260] Fix most strong mode warnings. The remaining warnings will need type-asserting wrappers to fix, which are coming in a separate CL. R=lrn@google.com Review URL: https://codereview.chromium.org//1841223002 . --- pkgs/async/.analysis_options | 2 + pkgs/async/CHANGELOG.md | 18 +++++ pkgs/async/lib/src/async_memoizer.dart | 2 +- pkgs/async/lib/src/cancelable_operation.dart | 6 +- pkgs/async/lib/src/delegate/future.dart | 8 +-- pkgs/async/lib/src/result.dart | 42 ++++------- .../lib/src/result/capture_transformer.dart | 5 +- pkgs/async/lib/src/result/error.dart | 8 +-- pkgs/async/lib/src/result/future.dart | 9 +-- pkgs/async/lib/src/result/value.dart | 2 +- .../src/single_subscription_transformer.dart | 17 ++++- pkgs/async/lib/src/stream_completer.dart | 10 +-- pkgs/async/lib/src/stream_group.dart | 10 +-- pkgs/async/lib/src/stream_queue.dart | 70 +++++++++---------- pkgs/async/lib/src/stream_sink_completer.dart | 5 +- .../handler_transformer.dart | 6 +- pkgs/async/lib/src/stream_splitter.dart | 14 ++-- pkgs/async/lib/src/stream_zip.dart | 16 ++--- pkgs/async/lib/src/subscription_stream.dart | 15 ++-- pkgs/async/pubspec.yaml | 4 +- 20 files changed, 143 insertions(+), 126 deletions(-) create mode 100644 pkgs/async/.analysis_options diff --git a/pkgs/async/.analysis_options b/pkgs/async/.analysis_options new file mode 100644 index 00000000..a10d4c5a --- /dev/null +++ b/pkgs/async/.analysis_options @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 946d3a8f..803b5899 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,21 @@ +## 1.9.1 + +* Fix all strong mode warnings and add generic method annotations. + +* `new StreamQueue()` now takes a `Stream` rather than a `Stream`. + Passing a type that wasn't `is`-compatible with `Stream` would already + throw an error under some circumstances, so this is not considered a breaking + change. + +* `new SubscriptionStream()` now takes a `Stream` rather than a + `Stream`. Passing a type that wasn't `is`-compatible with `Stream` + would already throw an error under some circumstances, so this is not + considered a breaking change. + +* `ErrorResult` now takes a type parameter. + +* `Result.asError` now returns a `Result`. + ## 1.9.0 * Deprecate top-level libraries other than `package:async/async.dart`, which diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index 3612a7d0..0213f68d 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -31,7 +31,7 @@ class AsyncMemoizer { /// /// This can be accessed at any time, and will fire once [runOnce] is called. Future get future => _completer.future; - final _completer = new Completer(); + final _completer = new Completer(); /// Whether [runOnce] has been called yet. bool get hasRun => _completer.isCompleted; diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 1ba6b3c4..d0a1871f 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -62,9 +62,9 @@ class CancelableOperation { /// If this operation is cancelled, the returned future waits for the future /// returned by [cancel], then completes to [cancellationValue]. Future valueOrCancellation([T cancellationValue]) { - var completer = new Completer.sync(); - - value.then(completer.complete, onError: completer.completeError); + var completer = new Completer.sync(); + value.then((result) => completer.complete(result), + onError: completer.completeError); _completer._cancelMemo.future.then((_) { completer.complete(cancellationValue); diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 5e84e4f6..bfc075b7 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -7,20 +7,20 @@ import 'dart:async'; /// A wrapper that forwards calls to a [Future]. class DelegatingFuture implements Future { /// The wrapped [Future]. - final Future _future; + final Future _future; DelegatingFuture(this._future); Stream asStream() => _future.asStream(); - Future catchError(Function onError, {bool test(error)}) => + Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); - Future then(onValue(T value), {Function onError}) => + Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => _future.then(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); - Future timeout(Duration timeLimit, {void onTimeout()}) => + Future timeout(Duration timeLimit, {void onTimeout()}) => _future.timeout(timeLimit, onTimeout: onTimeout); } diff --git a/pkgs/async/lib/src/result.dart b/pkgs/async/lib/src/result.dart index 482cf9ba..393dcedd 100644 --- a/pkgs/async/lib/src/result.dart +++ b/pkgs/async/lib/src/result.dart @@ -72,20 +72,14 @@ abstract class Result { factory Result.error(Object error, [StackTrace stackTrace]) => new ErrorResult(error, stackTrace); - // Helper functions. - static _captureValue(value) => new ValueResult(value); - static _captureError(error, stack) => new ErrorResult(error, stack); - static _release(Result v) { - if (v.isValue) return v.asValue.value; // Avoid wrapping in future. - return v.asFuture; - } - /// Capture the result of a future into a `Result` future. /// /// The resulting future will never have an error. /// Errors have been converted to an [ErrorResult] value. - static Future capture(Future future) { - return future.then(_captureValue, onError: _captureError); + static Future*/> capture/**/(Future/**/ future) { + return future.then((value) => new ValueResult(value), + onError: (error, stackTrace) => + new ErrorResult/**/(error, stackTrace)); } /// Release the result of a captured future. @@ -95,30 +89,23 @@ abstract class Result { /// /// If [future] completes with an error, the returned future completes with /// the same error. - static Future release(Future future) { - return future.then(_release); - } + static Future/**/ release/**/(Future*/> future) => + future.then/*>*/((result) => result.asFuture); /// Capture the results of a stream into a stream of [Result] values. /// /// The returned stream will not have any error events. /// Errors from the source stream have been converted to [ErrorResult]s. - /// - /// Shorthand for transforming the stream using [captureStreamTransformer]. - static Stream captureStream(Stream source) { - return source.transform(captureStreamTransformer); - } + static Stream*/> captureStream/**/(Stream/**/ source) => + source.transform(new CaptureStreamTransformer/**/()); /// Release a stream of [result] values into a stream of the results. /// /// `Result` values of the source stream become value or error events in /// the returned stream as appropriate. /// Errors from the source stream become errors in the returned stream. - /// - /// Shorthand for transforming the stream using [releaseStreamTransformer]. - static Stream releaseStream(Stream source) { - return source.transform(releaseStreamTransformer); - } + static Stream/**/ releaseStream/**/(Stream*/> source) => + source.transform(new ReleaseStreamTransformer/**/()); /// Converts a result of a result to a single result. /// @@ -126,9 +113,10 @@ abstract class Result { /// which is then an error, then a result with that error is returned. /// Otherwise both levels of results are value results, and a single /// result with the value is returned. - static Result flatten(Result result) { - if (result.isError) return result; - return result.asValue.value; + static Result/**/ flatten/**/(Result*/> result) { + if (result.isValue) return result.asValue.value; + return new ErrorResult/**/( + result.asError.error, result.asError.stackTrace); } /// Whether this result is a value result. @@ -149,7 +137,7 @@ abstract class Result { /// If this is an error result, return itself. /// /// Otherwise return `null`. - ErrorResult get asError; + ErrorResult get asError; /// Complete a completer with this result. void complete(Completer completer); diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index ca4d0d39..6b1bbf23 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -7,7 +7,10 @@ import 'dart:async'; import '../result.dart'; import 'capture_sink.dart'; -/// Use [Result.captureTransformer] instead. +/// A stream transformer that captures a stream of events into [Result]s. +/// +/// The result of the transformation is a stream of [Result] values and no +/// error events. This is the transformer used by [captureStream]. @Deprecated("Will be removed in async 2.0.0.") class CaptureStreamTransformer implements StreamTransformer> { const CaptureStreamTransformer(); diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index eecf68e4..b6d58593 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -8,14 +8,14 @@ import '../result.dart'; import 'value.dart'; /// A result representing a thrown error. -class ErrorResult implements Result { +class ErrorResult implements Result { final error; final StackTrace stackTrace; bool get isValue => false; bool get isError => true; - ValueResult get asValue => null; - ErrorResult get asError => this; + ValueResult get asValue => null; + ErrorResult get asError => this; ErrorResult(this.error, this.stackTrace); @@ -27,7 +27,7 @@ class ErrorResult implements Result { sink.addError(error, stackTrace); } - Future get asFuture => new Future.error(error, stackTrace); + Future get asFuture => new Future.error(error, stackTrace); /// Calls an error handler with the error and stacktrace. /// diff --git a/pkgs/async/lib/src/result/future.dart b/pkgs/async/lib/src/result/future.dart index db9dd822..209e8b1e 100644 --- a/pkgs/async/lib/src/result/future.dart +++ b/pkgs/async/lib/src/result/future.dart @@ -20,11 +20,12 @@ class ResultFuture extends DelegatingFuture { Result _result; factory ResultFuture(Future future) { - var resultFuture; - resultFuture = new ResultFuture._(Result.capture(future).then((result) { + ResultFuture resultFuture; + resultFuture = new ResultFuture._(() async { + var result = await Result.capture(future); resultFuture._result = result; - return result.asFuture; - })); + return await result.asFuture; + }()); return resultFuture; } diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart index f065ab67..39fa0481 100644 --- a/pkgs/async/lib/src/result/value.dart +++ b/pkgs/async/lib/src/result/value.dart @@ -14,7 +14,7 @@ class ValueResult implements Result { bool get isValue => true; bool get isError => false; ValueResult get asValue => this; - ErrorResult get asError => null; + ErrorResult get asError => null; ValueResult(this.value); diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index 4255175f..e01efac0 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -9,15 +9,26 @@ import 'dart:async'; /// /// This buffers the broadcast stream's events, which means that it starts /// listening to a stream as soon as it's bound. +/// +/// This also casts the source stream's events to type `T`. If the cast fails, +/// the result stream will emit a [CastError]. This behavior is deprecated, and +/// should not be relied upon. class SingleSubscriptionTransformer implements StreamTransformer { const SingleSubscriptionTransformer(); Stream bind(Stream stream) { var subscription; - var controller = new StreamController(sync: true, + var controller = new StreamController(sync: true, onCancel: () => subscription.cancel()); - subscription = stream.listen(controller.add, - onError: controller.addError, onDone: controller.close); + subscription = stream.listen((value) { + // TODO(nweiz): When we release a new major version, get rid of the second + // type parameter and avoid this conversion. + try { + controller.add(value as T); + } on CastError catch (error, stackTrace) { + controller.addError(error, stackTrace); + } + }, onError: controller.addError, onDone: controller.close); return controller.stream; } } diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index 73557b2a..953985e3 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -24,7 +24,7 @@ import "dart:async"; /// the listen is performed directly on the source stream. class StreamCompleter { /// The stream doing the actual work, is returned by [stream]. - final _CompleterStream _stream = new _CompleterStream(); + final _stream = new _CompleterStream(); /// Convert a `Future` to a `Stream`. /// @@ -34,8 +34,8 @@ class StreamCompleter { /// /// If the future completes with an error, the returned stream will /// instead contain just that error. - static Stream fromFuture(Future streamFuture) { - var completer = new StreamCompleter(); + static Stream/**/ fromFuture/**/(Future*/> streamFuture) { + var completer = new StreamCompleter/**/(); streamFuture.then(completer.setSourceStream, onError: completer.setError); return completer.stream; @@ -109,13 +109,13 @@ class _CompleterStream extends Stream { /// /// Created if the user listens on this stream before the source stream /// is set, or if using [_setEmpty] so there is no source stream. - StreamController _controller; + StreamController _controller; /// Source stream for the events provided by this stream. /// /// Set when the completer sets the source stream using [_setSourceStream] /// or [_setEmpty]. - Stream _sourceStream; + Stream _sourceStream; StreamSubscription listen(onData(T data), {Function onError, diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 6239fc23..889ccd82 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -12,8 +12,8 @@ import 'dart:async'; /// this means that events emitted by broadcast streams will be dropped until /// [stream] has a listener.** /// -/// If the `StreamGroup` is constructed using [new StreamGroup], [stream] will be -/// single-subscription. In this case, if [stream] is paused or canceled, all +/// If the `StreamGroup` is constructed using [new StreamGroup], [stream] will +/// be single-subscription. In this case, if [stream] is paused or canceled, all /// streams in the group will likewise be paused or canceled, respectively. /// /// If the `StreamGroup` is constructed using [new StreamGroup.broadcast], @@ -53,8 +53,8 @@ class StreamGroup implements Sink> { /// /// This is equivalent to adding [streams] to a group, closing that group, and /// returning its stream. - static Stream merge(Iterable streams) { - var group = new StreamGroup(); + static Stream/**/ merge/**/(Iterable*/> streams) { + var group = new StreamGroup/**/(); streams.forEach(group.add); group.close(); return group.stream; @@ -192,7 +192,7 @@ class StreamGroup implements Sink> { /// Starts actively forwarding events from [stream] to [_controller]. /// /// This will pause the resulting subscription if [this] is paused. - StreamSubscription _listenToStream(Stream stream) { + StreamSubscription _listenToStream(Stream stream) { var subscription = stream.listen( _controller.add, onError: _controller.addError, diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 8482e0c6..4ce9e130 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -94,7 +94,7 @@ abstract class StreamQueue { final Queue<_EventRequest> _requestQueue = new Queue(); /// Create a `StreamQueue` of the events of [source]. - factory StreamQueue(Stream source) = _StreamQueue; + factory StreamQueue(Stream source) = _StreamQueue; StreamQueue._(); @@ -274,7 +274,7 @@ abstract class StreamQueue { /// Can only be used by the very last request (the stream queue must /// be closed by that request). /// Only used by [rest]. - Stream _extractStream(); + Stream _extractStream(); /// Requests that the event source pauses events. /// @@ -342,13 +342,13 @@ abstract class StreamQueue { /// to when a request needs events. class _StreamQueue extends StreamQueue { /// Source of events. - final Stream _sourceStream; + final Stream _sourceStream; /// Subscription on [_sourceStream] while listening for events. /// /// Set to subscription when listening, and set to `null` when the /// subscription is done (and [_isDone] is set to true). - StreamSubscription _subscription; + StreamSubscription _subscription; _StreamQueue(this._sourceStream) : super._(); @@ -422,7 +422,7 @@ class _StreamQueue extends StreamQueue { /// /// The [close] method is also called immediately when the source stream /// is done. -abstract class _EventRequest { +abstract class _EventRequest { /// Handle available events. /// /// The available events are provided as a queue. The `update` function @@ -443,22 +443,22 @@ abstract class _EventRequest { /// If the function returns `false` when the stream has already closed /// ([isDone] is true), then the request must call /// [StreamQueue._updateRequests] itself when it's ready to continue. - bool update(Queue events, bool isDone); + bool update(Queue> events, bool isDone); } /// Request for a [StreamQueue.next] call. /// /// Completes the returned future when receiving the first event, /// and is then complete. -class _NextRequest implements _EventRequest { +class _NextRequest implements _EventRequest { /// Completer for the future returned by [StreamQueue.next]. - final Completer _completer; + final _completer = new Completer(); - _NextRequest() : _completer = new Completer(); + _NextRequest(); Future get future => _completer.future; - bool update(Queue events, bool isDone) { + bool update(Queue> events, bool isDone) { if (events.isNotEmpty) { events.removeFirst().complete(_completer); return true; @@ -474,9 +474,9 @@ class _NextRequest implements _EventRequest { } /// Request for a [StreamQueue.skip] call. -class _SkipRequest implements _EventRequest { +class _SkipRequest implements _EventRequest { /// Completer for the future returned by the skip call. - final Completer _completer = new Completer(); + final _completer = new Completer(); /// Number of remaining events to skip. /// @@ -489,9 +489,9 @@ class _SkipRequest implements _EventRequest { _SkipRequest(this._eventsToSkip); /// The future completed when the correct number of events have been skipped. - Future get future => _completer.future; + Future get future => _completer.future; - bool update(Queue events, bool isDone) { + bool update(Queue> events, bool isDone) { while (_eventsToSkip > 0) { if (events.isEmpty) { if (isDone) break; @@ -501,7 +501,7 @@ class _SkipRequest implements _EventRequest { var event = events.removeFirst(); if (event.isError) { - event.complete(_completer); + _completer.completeError(event.asError.error, event.asError.stackTrace); return true; } } @@ -511,12 +511,12 @@ class _SkipRequest implements _EventRequest { } /// Request for a [StreamQueue.take] call. -class _TakeRequest implements _EventRequest { +class _TakeRequest implements _EventRequest { /// Completer for the future returned by the take call. - final Completer _completer; + final _completer = new Completer>(); /// List collecting events until enough have been seen. - final List _list = []; + final _list = []; /// Number of events to capture. /// @@ -524,24 +524,24 @@ class _TakeRequest implements _EventRequest { /// this value. final int _eventsToTake; - _TakeRequest(this._eventsToTake) : _completer = new Completer>(); + _TakeRequest(this._eventsToTake); /// The future completed when the correct number of events have been captured. - Future get future => _completer.future; + Future> get future => _completer.future; - bool update(Queue events, bool isDone) { + bool update(Queue> events, bool isDone) { while (_list.length < _eventsToTake) { if (events.isEmpty) { if (isDone) break; return false; } - var result = events.removeFirst(); - if (result.isError) { - result.complete(_completer); + var event = events.removeFirst(); + if (event.isError) { + _completer.completeError(event.asError.error, event.asError.stackTrace); return true; } - _list.add(result.asValue.value); + _list.add(event.asValue.value); } _completer.complete(_list); return true; @@ -553,9 +553,9 @@ class _TakeRequest implements _EventRequest { /// The request needs no events, it just waits in the request queue /// until all previous events are fulfilled, then it cancels the stream queue /// source subscription. -class _CancelRequest implements _EventRequest { +class _CancelRequest implements _EventRequest { /// Completer for the future returned by the `cancel` call. - final Completer _completer = new Completer(); + final _completer = new Completer(); /// The [StreamQueue] object that has this request queued. /// @@ -568,7 +568,7 @@ class _CancelRequest implements _EventRequest { /// The future completed when the cancel request is completed. Future get future => _completer.future; - bool update(Queue events, bool isDone) { + bool update(Queue> events, bool isDone) { if (_streamQueue._isDone) { _completer.complete(); } else { @@ -584,22 +584,22 @@ class _CancelRequest implements _EventRequest { /// The request is always complete, it just waits in the request queue /// until all previous events are fulfilled, then it takes over the /// stream events subscription and creates a stream from it. -class _RestRequest implements _EventRequest { +class _RestRequest implements _EventRequest { /// Completer for the stream returned by the `rest` call. - final StreamCompleter _completer = new StreamCompleter(); + final _completer = new StreamCompleter(); /// The [StreamQueue] object that has this request queued. /// /// When the event is completed, it needs to cancel the active subscription /// of the `StreamQueue` object, if any. - final StreamQueue _streamQueue; + final StreamQueue _streamQueue; _RestRequest(this._streamQueue); /// The stream which will contain the remaining events of [_streamQueue]. Stream get stream => _completer.stream; - bool update(Queue events, bool isDone) { + bool update(Queue> events, bool isDone) { if (events.isEmpty) { if (_streamQueue._isDone) { _completer.setEmpty(); @@ -627,12 +627,12 @@ class _RestRequest implements _EventRequest { /// but doesn't consume the event. /// If the request is closed without seeing an event, then /// the [future] is completed with `false`. -class _HasNextRequest implements _EventRequest { - final Completer _completer = new Completer(); +class _HasNextRequest implements _EventRequest { + final _completer = new Completer(); Future get future => _completer.future; - bool update(Queue events, bool isDone) { + bool update(Queue> events, bool isDone) { if (events.isNotEmpty) { _completer.complete(true); return true; diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index b53a8d23..00a7086c 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -36,8 +36,9 @@ class StreamSinkCompleter { /// /// If the future completes with an error, the returned sink will instead /// be closed. Its [Sink.done] future will contain the error. - static StreamSink fromFuture(Future sinkFuture) { - var completer = new StreamSinkCompleter(); + static StreamSink/**/ fromFuture/**/( + Future*/> sinkFuture) { + var completer = new StreamSinkCompleter/**/(); sinkFuture.then(completer.setDestinationSink, onError: completer.setError); return completer.sink; diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index a20a3fa1..8cc3d011 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -54,11 +54,7 @@ class _HandlerSink implements StreamSink { void add(S event) { if (_transformer._handleData == null) { - // [event] is an S and [_inner.add] takes a T. This style of conversion - // will throw an error in checked mode if [_inner] is actually a - // [StreamSink], but will work if [_inner] isn't reified and won't add - // an extra check in unchecked mode. - _inner.add(event as dynamic); + _inner.add(event as T); } else { _transformer._handleData(event, _safeCloseInner); } diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index ec648aad..448fe2a2 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -57,10 +57,11 @@ class StreamSplitter { /// /// [count] defaults to 2. This is the same as creating [count] branches and /// then closing the [StreamSplitter]. - static List splitFrom(Stream stream, [int count]) { + static List*/> splitFrom/**/(Stream/**/ stream, + [int count]) { if (count == null) count = 2; - var splitter = new StreamSplitter(stream); - var streams = new List.generate(count, (_) => splitter.split()); + var splitter = new StreamSplitter/**/(stream); + var streams = new List.generate(count, (_) => splitter.split()); splitter.close(); return streams; } @@ -75,12 +76,11 @@ class StreamSplitter { throw new StateError("Can't call split() on a closed StreamSplitter."); } - var controller; - controller = new StreamController( + var controller = new StreamController( onListen: _onListen, onPause: _onPause, - onResume: _onResume, - onCancel: () => _onCancel(controller)); + onResume: _onResume); + controller.onCancel = () => _onCancel(controller); for (var result in _buffer) { result.addTo(controller); diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index 432cc22c..f9e20825 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -17,22 +17,22 @@ class StreamZip extends Stream> { StreamZip(Iterable> streams) : _streams = streams; - StreamSubscription> listen(void onData(List data), { + StreamSubscription> listen(void onData(List data), { Function onError, void onDone(), bool cancelOnError}) { cancelOnError = identical(true, cancelOnError); - List subscriptions = []; - StreamController controller; - List current; + var subscriptions = >[]; + StreamController> controller; + List current; int dataCount = 0; /// Called for each data from a subscription in [subscriptions]. - void handleData(int index, data) { + void handleData(int index, T data) { current[index] = data; dataCount++; if (dataCount == subscriptions.length) { - List data = current; + var data = current; current = new List(subscriptions.length); dataCount = 0; for (int i = 0; i < subscriptions.length; i++) { @@ -70,7 +70,7 @@ class StreamZip extends Stream> { } try { - for (Stream stream in _streams) { + for (var stream in _streams) { int index = subscriptions.length; subscriptions.add(stream.listen( (data) { handleData(index, data); }, @@ -87,7 +87,7 @@ class StreamZip extends Stream> { current = new List(subscriptions.length); - controller = new StreamController( + controller = new StreamController>( onPause: () { for (int i = 0; i < subscriptions.length; i++) { // This may pause some subscriptions more than once. diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 4f58c76f..c448620c 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -18,7 +18,7 @@ import "delegate/stream_subscription.dart"; /// If other code is accessing the subscription, results may be unpredictable. class SubscriptionStream extends Stream { /// The subscription providing the events for this stream. - StreamSubscription _source; + StreamSubscription _source; /// Create a single-subscription `Stream` from [subscription]. /// @@ -29,7 +29,7 @@ class SubscriptionStream extends Stream { /// If the `subscription` doesn't send any `done` events, neither will this /// stream. That may be an issue if `subscription` was made to cancel on /// an error. - SubscriptionStream(StreamSubscription subscription) + SubscriptionStream(StreamSubscription subscription) : _source = subscription { _source.pause(); // Clear callbacks to avoid keeping them alive unnecessarily. @@ -48,13 +48,10 @@ class SubscriptionStream extends Stream { cancelOnError = (true == cancelOnError); var subscription = _source; _source = null; - var result; - if (cancelOnError) { - result = new _CancelOnErrorSubscriptionWrapper(subscription); - } else { - // Wrap the subscription to ensure correct type parameter. - result = new DelegatingStreamSubscription(subscription); - } + + var result = cancelOnError + ? new _CancelOnErrorSubscriptionWrapper(subscription) + : subscription; result.onData(onData); result.onError(onError); result.onDone(onDone); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 2430d752..9e64244a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.9.0 +version: 1.9.1-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async @@ -8,4 +8,4 @@ dev_dependencies: stack_trace: "^1.0.0" test: "^0.12.0" environment: - sdk: ">=1.9.0 <2.0.0" + sdk: ">=1.12.0 <2.0.0" From 8c40cd6922462a898085a217483370702553c121 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 12 Apr 2016 13:42:13 -0700 Subject: [PATCH 062/260] Add typed wrapper functions to delegate classes. These mirror the methods in the the collection package, and serve a similar purpose of safely casting generic objects when the user is confident that the actual object's values are more specific than the static type. R=floitsch@google.com, lrn@google.com Review URL: https://codereview.chromium.org//1870543004 . --- pkgs/async/CHANGELOG.md | 25 +- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/delegate/event_sink.dart | 13 +- pkgs/async/lib/src/delegate/future.dart | 13 +- pkgs/async/lib/src/delegate/sink.dart | 14 +- pkgs/async/lib/src/delegate/stream.dart | 28 + .../lib/src/delegate/stream_consumer.dart | 16 +- pkgs/async/lib/src/delegate/stream_sink.dart | 14 +- .../lib/src/delegate/stream_subscription.dart | 17 +- pkgs/async/lib/src/lazy_stream.dart | 12 +- pkgs/async/lib/src/subscription_stream.dart | 2 +- pkgs/async/lib/src/typed/future.dart | 25 + pkgs/async/lib/src/typed/stream.dart | 136 ++++ .../lib/src/typed/stream_subscription.dart | 36 ++ pkgs/async/lib/src/utils.dart | 7 + pkgs/async/pubspec.yaml | 4 +- .../async/test/typed_wrapper/future_test.dart | 117 ++++ .../stream_subscription_test.dart | 144 +++++ .../async/test/typed_wrapper/stream_test.dart | 598 ++++++++++++++++++ pkgs/async/test/utils.dart | 23 + 20 files changed, 1223 insertions(+), 22 deletions(-) create mode 100644 pkgs/async/lib/src/delegate/stream.dart create mode 100644 pkgs/async/lib/src/typed/future.dart create mode 100644 pkgs/async/lib/src/typed/stream.dart create mode 100644 pkgs/async/lib/src/typed/stream_subscription.dart create mode 100644 pkgs/async/lib/src/utils.dart create mode 100644 pkgs/async/test/typed_wrapper/future_test.dart create mode 100644 pkgs/async/test/typed_wrapper/stream_subscription_test.dart create mode 100644 pkgs/async/test/typed_wrapper/stream_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 803b5899..b18365fc 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,14 +1,23 @@ -## 1.9.1 +## 1.10.0 -* Fix all strong mode warnings and add generic method annotations. +* Add `DelegatingFuture.typed()`, `DelegatingStreamSubscription.typed()`, + `DelegatingStreamConsumer.typed()`, `DelegatingSink.typed()`, + `DelegatingEventSink.typed()`, and `DelegatingStreamSink.typed()` static + methods. These wrap untyped instances of these classes with the correct type + parameter, and assert the types of values as they're accessed. + +* Add a `DelegatingStream` class. This is behaviorally identical to `StreamView` + from `dart:async`, but it follows this package's naming conventions and + provides a `DelegatingStream.typed()` static method. -* `new StreamQueue()` now takes a `Stream` rather than a `Stream`. - Passing a type that wasn't `is`-compatible with `Stream` would already - throw an error under some circumstances, so this is not considered a breaking - change. +* Fix all strong mode warnings and add generic method annotations. -* `new SubscriptionStream()` now takes a `Stream` rather than a - `Stream`. Passing a type that wasn't `is`-compatible with `Stream` +* `new StreamQueue()`, `new SubscriptionStream()`, `new + DelegatingStreamSubscription()`, `new DelegatingStreamConsumer()`, `new + DelegatingSink()`, `new DelegatingEventSink()`, and `new + DelegatingStreamSink()` now take arguments with generic type arguments (for + example `Stream`) rather than without (for example `Stream`). + Passing a type that wasn't `is`-compatible with the fully-specified generic would already throw an error under some circumstances, so this is not considered a breaking change. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 0d3e051c..70553410 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -7,6 +7,7 @@ export "src/cancelable_operation.dart"; export "src/delegate/event_sink.dart"; export "src/delegate/future.dart"; export "src/delegate/sink.dart"; +export "src/delegate/stream.dart"; export "src/delegate/stream_consumer.dart"; export "src/delegate/stream_sink.dart"; export "src/delegate/stream_subscription.dart"; diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 5a525dfc..14501b28 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -12,7 +12,18 @@ class DelegatingEventSink implements EventSink { final EventSink _sink; /// Create a delegating sink forwarding calls to [sink]. - DelegatingEventSink(EventSink sink) : _sink = sink; + DelegatingEventSink(EventSink sink) : _sink = sink; + + DelegatingEventSink._(this._sink); + + /// Creates a wrapper that coerces the type of [sink]. + /// + /// Unlike [new DelegatingEventSink], this only requires its argument to be an + /// instance of `EventSink`, not `EventSink`. This means that calls to + /// [add] may throw a [CastError] if the argument type doesn't match the + /// reified type of [sink]. + static EventSink/**/ typed/**/(EventSink sink) => + sink is EventSink/**/ ? sink : new DelegatingEventSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index bfc075b7..3f7ea72d 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -4,6 +4,8 @@ import 'dart:async'; +import '../typed/future.dart'; + /// A wrapper that forwards calls to a [Future]. class DelegatingFuture implements Future { /// The wrapped [Future]. @@ -11,6 +13,15 @@ class DelegatingFuture implements Future { DelegatingFuture(this._future); + /// Creates a wrapper which throws if [future]'s value isn't an instance of + /// `T`. + /// + /// This soundly converts a [Future] to a `Future`, regardless of its + /// original generic type, by asserting that its value is an instance of `T` + /// whenever it's provided. If it's not, the future throws a [CastError]. + static Future/**/ typed/**/(Future future) => + future is Future/**/ ? future : new TypeSafeFuture/**/(future); + Stream asStream() => _future.asStream(); Future catchError(Function onError, {bool test(Object error)}) => @@ -21,6 +32,6 @@ class DelegatingFuture implements Future { Future whenComplete(action()) => _future.whenComplete(action); - Future timeout(Duration timeLimit, {void onTimeout()}) => + Future timeout(Duration timeLimit, {onTimeout()}) => _future.timeout(timeLimit, onTimeout: onTimeout); } diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index cee29375..326c15bd 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -10,8 +10,18 @@ class DelegatingSink implements Sink { final Sink _sink; /// Create a delegating sink forwarding calls to [sink]. - DelegatingSink(Sink sink) - : _sink = sink; + DelegatingSink(Sink sink) : _sink = sink; + + DelegatingSink._(this._sink); + + /// Creates a wrapper that coerces the type of [sink]. + /// + /// Unlike [new DelegatingSink], this only requires its argument to be an + /// instance of `Sink`, not `Sink`. This means that calls to [add] may + /// throw a [CastError] if the argument type doesn't match the reified type of + /// [sink]. + static Sink/**/ typed/**/(Sink sink) => + sink is Sink/**/ ? sink : new DelegatingSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/stream.dart b/pkgs/async/lib/src/delegate/stream.dart new file mode 100644 index 00000000..75622187 --- /dev/null +++ b/pkgs/async/lib/src/delegate/stream.dart @@ -0,0 +1,28 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../typed/stream.dart'; + +/// Simple delegating wrapper around a [Stream]. +/// +/// Subclasses can override individual methods, or use this to expose only the +/// [Stream] methods of a subclass. +/// +/// Note that this is identical to [StreamView] in `dart:async`. It's provided +/// under this name for consistency with other `Delegating*` classes. +class DelegatingStream extends StreamView { + DelegatingStream(Stream stream) : super(stream); + + /// Creates a wrapper which throws if [stream]'s events aren't instances of + /// `T`. + /// + /// This soundly converts a [Stream] to a `Stream`, regardless of its + /// original generic type, by asserting that its events are instances of `T` + /// whenever they're provided. If they're not, the stream throws a + /// [CastError]. + static Stream/**/ typed/**/(Stream stream) => + stream is Stream/**/ ? stream : new TypeSafeStream/**/(stream); +} diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index dcaf0c25..4f495d0e 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -12,8 +12,20 @@ class DelegatingStreamConsumer implements StreamConsumer { final StreamConsumer _consumer; /// Create a delegating consumer forwarding calls to [consumer]. - DelegatingStreamConsumer(StreamConsumer consumer) - : _consumer = consumer; + DelegatingStreamConsumer(StreamConsumer consumer) : _consumer = consumer; + + DelegatingStreamConsumer._(this._consumer); + + /// Creates a wrapper that coerces the type of [consumer]. + /// + /// Unlike [new StreamConsumer], this only requires its argument to be an + /// instance of `StreamConsumer`, not `StreamConsumer`. This means that + /// calls to [addStream] may throw a [CastError] if the argument type doesn't + /// match the reified type of [consumer]. + static StreamConsumer/**/ typed/**/(StreamConsumer consumer) => + consumer is StreamConsumer/**/ + ? consumer + : new DelegatingStreamConsumer._(consumer); Future addStream(Stream stream) => _consumer.addStream(stream); diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index e06afc1b..9b52b19e 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -14,8 +14,18 @@ class DelegatingStreamSink implements StreamSink { Future get done => _sink.done; /// Create delegating sink forwarding calls to [sink]. - DelegatingStreamSink(StreamSink sink) - : _sink = sink; + DelegatingStreamSink(StreamSink sink) : _sink = sink; + + DelegatingStreamSink._(this._sink); + + /// Creates a wrapper that coerces the type of [sink]. + /// + /// Unlike [new StreamSink], this only requires its argument to be an instance + /// of `StreamSink`, not `StreamSink`. This means that calls to [add] may + /// throw a [CastError] if the argument type doesn't match the reified type of + /// [sink]. + static StreamSink/**/ typed/**/(StreamSink sink) => + sink is StreamSink/**/ ? sink : new DelegatingStreamSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index e7153b2a..b86e91f9 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -4,6 +4,8 @@ import 'dart:async'; +import '../typed/stream_subscription.dart'; + /// Simple delegating wrapper around a [StreamSubscription]. /// /// Subclasses can override individual methods. @@ -11,9 +13,22 @@ class DelegatingStreamSubscription implements StreamSubscription { final StreamSubscription _source; /// Create delegating subscription forwarding calls to [sourceSubscription]. - DelegatingStreamSubscription(StreamSubscription sourceSubscription) + DelegatingStreamSubscription(StreamSubscription sourceSubscription) : _source = sourceSubscription; + /// Creates a wrapper which throws if [subscription]'s events aren't instances + /// of `T`. + /// + /// This soundly converts a [StreamSubscription] to a `StreamSubscription`, + /// regardless of its original generic type, by asserting that its events are + /// instances of `T` whenever they're provided. If they're not, the + /// subscription throws a [CastError]. + static StreamSubscription/**/ typed/**/( + StreamSubscription subscription) => + subscription is StreamSubscription/**/ + ? subscription + : new TypeSafeStreamSubscription/**/(subscription); + void onData(void handleData(T data)) { _source.onData(handleData); } diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index 3ba49531..147fa8bd 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -5,6 +5,7 @@ import "dart:async"; import "stream_completer.dart"; +import "delegate/stream.dart"; /// A [Stream] wrapper that forwards to another [Stream] that's initialized /// lazily. @@ -39,9 +40,14 @@ class LazyStream extends Stream { _callback = null; var result = callback(); - Stream stream = result is Future - ? StreamCompleter.fromFuture(result) - : result; + Stream stream; + if (result is Future) { + stream = StreamCompleter.fromFuture(result.then((stream) { + return DelegatingStream.typed/**/(stream as Stream); + })); + } else { + stream = DelegatingStream.typed/**/(result as Stream); + } return stream.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index c448620c..50ca81b6 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -68,7 +68,7 @@ class SubscriptionStream extends Stream { /// source subscription on the first error. class _CancelOnErrorSubscriptionWrapper extends DelegatingStreamSubscription { - _CancelOnErrorSubscriptionWrapper(StreamSubscription subscription) + _CancelOnErrorSubscriptionWrapper(StreamSubscription subscription) : super(subscription); void onError(Function handleError) { diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart new file mode 100644 index 00000000..a269593d --- /dev/null +++ b/pkgs/async/lib/src/typed/future.dart @@ -0,0 +1,25 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +class TypeSafeFuture implements Future { + final Future _future; + + TypeSafeFuture(this._future); + + Stream asStream() => _future.then((value) => value as T).asStream(); + + Future catchError(Function onError, {bool test(Object error)}) => + _future.catchError(onError, test: test); + + Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => + _future.then((value) => onValue(value as T), onError: onError); + + Future whenComplete(action()) => + new TypeSafeFuture(_future.whenComplete(action)); + + Future timeout(Duration timeLimit, {onTimeout()}) => + new TypeSafeFuture(_future.timeout(timeLimit, onTimeout: onTimeout)); +} diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart new file mode 100644 index 00000000..3db9f699 --- /dev/null +++ b/pkgs/async/lib/src/typed/stream.dart @@ -0,0 +1,136 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'package:collection/collection.dart'; + +import '../utils.dart'; +import 'stream_subscription.dart'; + +class TypeSafeStream implements Stream { + final Stream _stream; + + Future get first async => (await _stream.first) as T; + Future get last async => (await _stream.last) as T; + Future get single async => (await _stream.single) as T; + + bool get isBroadcast => _stream.isBroadcast; + Future get isEmpty => _stream.isEmpty; + Future get length => _stream.length; + + TypeSafeStream(this._stream); + + Stream asBroadcastStream( + {void onListen(StreamSubscription subscription), + void onCancel(StreamSubscription subscription)}) { + return new TypeSafeStream(_stream.asBroadcastStream( + onListen: onListen == null + ? null + : (subscription) => + onListen(new TypeSafeStreamSubscription(subscription)), + onCancel: onCancel == null + ? null + : (subscription) => + onCancel(new TypeSafeStreamSubscription(subscription)))); + } + + // TODO(nweiz): Give this a generic parameter when sdk#26125 is fixed. + Stream asyncExpand(Stream convert(T event)) => + _stream.asyncExpand(_validateType(convert)); + + // TODO(nweiz): Give this a generic parameter when sdk#26125 is fixed. + Stream asyncMap(convert(T event)) => _stream.asyncMap(_validateType(convert)); + + Stream distinct([bool equals(T previous, T next)]) => + new TypeSafeStream(_stream.distinct(equals == null + ? null + : (previous, next) => equals(previous as T, next as T))); + + // TODO(nweiz): Give this a generic parameter when sdk#26125 is fixed. + Future drain([futureValue]) => _stream.drain(futureValue); + + Stream/**/ expand/**/(Iterable/**/ convert(T value)) => + _stream.expand(_validateType(convert)); + + Future firstWhere(bool test(T element), {Object defaultValue()}) => + _stream.firstWhere(_validateType(test), defaultValue: defaultValue); + + Future lastWhere(bool test(T element), {Object defaultValue()}) => + _stream.lastWhere(_validateType(test), defaultValue: defaultValue); + + Future singleWhere(bool test(T element)) async => + (await _stream.singleWhere(_validateType(test))) as T; + + Future/**/ fold/**/(/*=S*/ initialValue, + /*=S*/ combine(/*=S*/ previous, T element)) => + _stream.fold(initialValue, + (previous, element) => combine(previous, element as T)); + + Future forEach(void action(T element)) => + _stream.forEach(_validateType(action)); + + Stream handleError(Function onError, {bool test(error)}) => + new TypeSafeStream(_stream.handleError(onError, test: test)); + + StreamSubscription listen(void onData(T value), + {Function onError, void onDone(), bool cancelOnError}) => + new TypeSafeStreamSubscription(_stream.listen(_validateType(onData), + onError: onError, onDone: onDone, cancelOnError: cancelOnError)); + + Stream/**/ map/**/(/*=S*/ convert(T event)) => + _stream.map(_validateType(convert)); + + // Don't forward to `_stream.pipe` because we want the consumer to see the + // type-asserted stream. + Future pipe(StreamConsumer consumer) => + consumer.addStream(this).then((_) => consumer.close()); + + Future reduce(T combine(T previous, T element)) async { + var result = await _stream.reduce( + (previous, element) => combine(previous as T, element as T)); + return result as T; + } + + Stream skipWhile(bool test(T element)) => + new TypeSafeStream(_stream.skipWhile(_validateType(test))); + + Stream takeWhile(bool test(T element)) => + new TypeSafeStream(_stream.takeWhile(_validateType(test))); + + Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => + _stream.timeout(timeLimit, onTimeout: onTimeout); + + Future> toList() async => + DelegatingList.typed/**/(await _stream.toList()); + + Future> toSet() async => + DelegatingSet.typed/**/(await _stream.toSet()); + + // Don't forward to `_stream.transform` because we want the transformer to see + // the type-asserted stream. + Stream/**/ transform/**/( + StreamTransformer transformer) => + transformer.bind(this); + + Stream where(bool test(T element)) => + new TypeSafeStream(_stream.where(_validateType(test))); + + Future every(bool test(T element)) => + _stream.every(_validateType(test)); + + Future any(bool test(T element)) => _stream.any(_validateType(test)); + Stream skip(int count) => new TypeSafeStream(_stream.skip(count)); + Stream take(int count) => new TypeSafeStream(_stream.take(count)); + Future elementAt(int index) async => (await _stream.elementAt(index)) as T; + Future contains(Object needle) => _stream.contains(needle); + Future join([String separator = ""]) => _stream.join(separator); + String toString() => _stream.toString(); + + /// Returns a version of [function] that asserts that its argument is an + /// instance of `T`. + UnaryFunction/**/ _validateType/**/( + /*=S*/ function(T value)) => + function == null ? null : (value) => function(value as T); +} diff --git a/pkgs/async/lib/src/typed/stream_subscription.dart b/pkgs/async/lib/src/typed/stream_subscription.dart new file mode 100644 index 00000000..800a4bad --- /dev/null +++ b/pkgs/async/lib/src/typed/stream_subscription.dart @@ -0,0 +1,36 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +class TypeSafeStreamSubscription implements StreamSubscription { + final StreamSubscription _subscription; + + bool get isPaused => _subscription.isPaused; + + TypeSafeStreamSubscription(this._subscription); + + void onData(void handleData(T data)) { + _subscription.onData((data) => handleData(data as T)); + } + + void onError(Function handleError) { + _subscription.onError(handleError); + } + + void onDone(void handleDone()) { + _subscription.onDone(handleDone); + } + + void pause([Future resumeFuture]) { + _subscription.pause(resumeFuture); + } + + void resume() { + _subscription.resume(); + } + + Future cancel() => _subscription.cancel(); + Future asFuture([futureValue]) => _subscription.asFuture(futureValue); +} diff --git a/pkgs/async/lib/src/utils.dart b/pkgs/async/lib/src/utils.dart new file mode 100644 index 00000000..0066003d --- /dev/null +++ b/pkgs/async/lib/src/utils.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2016, 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. + +/// A generic typedef for a function that takes one type and returns another. +typedef F UnaryFunction(E argument); + diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 9e64244a..0859e291 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,8 +1,10 @@ name: async -version: 1.9.1-dev +version: 1.10.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async +dependencies: + collection: "^1.5.0" dev_dependencies: fake_async: "^0.1.2" stack_trace: "^1.0.0" diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart new file mode 100644 index 00000000..6601ac1a --- /dev/null +++ b/pkgs/async/test/typed_wrapper/future_test.dart @@ -0,0 +1,117 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import "package:async/async.dart"; +import "package:async/src/typed/future.dart"; +import "package:test/test.dart"; + +import '../utils.dart'; + +void main() { + group("with valid types, forwards", () { + var wrapper; + var errorWrapper; + setUp(() { + wrapper = new TypeSafeFuture(new Future.value(12)); + + var error = new Future.error("oh no"); + error.catchError((_) {}); // Don't let the error cause the test to fail. + errorWrapper = new TypeSafeFuture(error); + }); + + test("asStream()", () { + expect(wrapper.asStream().toList(), completion(equals([12]))); + expect(errorWrapper.asStream().first, throwsA("oh no")); + }); + + test("catchError()", () { + expect( + wrapper.catchError(expectAsync((_) {}, count: 0), + test: expectAsync((_) {}, count: 0)), + completion(equals(12))); + + expect(errorWrapper.catchError(expectAsync((error) { + expect(error, equals("oh no")); + return "value"; + }), test: expectAsync((error) { + expect(error, equals("oh no")); + return true; + })), completion(equals("value"))); + }); + + test("then()", () { + expect(wrapper.then((value) => value.toString()), + completion(equals("12"))); + expect(errorWrapper.then(expectAsync((_) {}, count: 0)), + throwsA("oh no")); + }); + + test("whenComplete()", () { + expect(wrapper.whenComplete(expectAsync(() {})), completion(equals(12))); + expect(errorWrapper.whenComplete(expectAsync(() {})), throwsA("oh no")); + }); + + test("timeout()", () { + expect(wrapper.timeout(new Duration(seconds: 1)), completion(equals(12))); + expect(errorWrapper.timeout(new Duration(seconds: 1)), throwsA("oh no")); + + expect( + new TypeSafeFuture(new Completer().future) + .timeout(Duration.ZERO), + throwsA(new isInstanceOf())); + + expect( + new TypeSafeFuture(new Completer().future) + .timeout(Duration.ZERO, onTimeout: expectAsync(() => 15)), + completion(equals(15))); + }); + }); + + group("with invalid types", () { + var wrapper; + setUp(() { + wrapper = new TypeSafeFuture(new Future.value("foo")); + }); + + group("throws a CastError for", () { + test("asStream()", () { + expect(wrapper.asStream().first, throwsCastError); + }); + + test("then()", () { + expect( + wrapper.then(expectAsync((_) {}, count: 0), + onError: expectAsync((_) {}, count: 0)), + throwsCastError); + }); + + test("whenComplete()", () { + expect(wrapper.whenComplete(expectAsync(() {})).then((_) {}), + throwsCastError); + }); + + test("timeout()", () { + expect(wrapper.timeout(new Duration(seconds: 3)).then((_) {}), + throwsCastError); + + expect( + new TypeSafeFuture(new Completer().future) + .timeout(Duration.ZERO, onTimeout: expectAsync(() => "foo")) + .then((_) {}), + throwsCastError); + }); + }); + + group("doesn't throw a CastError for", () { + test("catchError()", () { + // catchError has a Future return type, so even if there's no + // error we don't re-wrap the returned future. + expect(wrapper.catchError(expectAsync((_) {}, count: 0)), + completion(equals("foo"))); + }); + }); + }); +} diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart new file mode 100644 index 00000000..30d38922 --- /dev/null +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -0,0 +1,144 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import "package:async/async.dart"; +import "package:async/src/typed/stream_subscription.dart"; +import "package:test/test.dart"; + +import '../utils.dart'; + +void main() { + group("with valid types, forwards", () { + var controller; + var wrapper; + var isCanceled; + setUp(() { + controller = new StreamController(onCancel: () { + isCanceled = true; + }); + wrapper = new TypeSafeStreamSubscription( + controller.stream.listen(null)); + }); + + test("onData()", () { + wrapper.onData(expectAsync((data) { + expect(data, equals(1)); + })); + controller.add(1); + }); + + test("onError()", () { + wrapper.onError(expectAsync((error) { + expect(error, equals("oh no")); + })); + controller.addError("oh no"); + }); + + test("onDone()", () { + wrapper.onDone(expectAsync(() {})); + controller.close(); + }); + + test("pause(), resume(), and isPaused", () async { + expect(wrapper.isPaused, isFalse); + + wrapper.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + expect(wrapper.isPaused, isTrue); + + wrapper.resume(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + expect(wrapper.isPaused, isFalse); + }); + + test("cancel()", () async { + wrapper.cancel(); + await flushMicrotasks(); + expect(isCanceled, isTrue); + }); + + test("asFuture()", () { + expect(wrapper.asFuture(12), completion(equals(12))); + controller.close(); + }); + }); + + group("with invalid types,", () { + var controller; + var wrapper; + var isCanceled; + setUp(() { + controller = new StreamController(onCancel: () { + isCanceled = true; + }); + wrapper = new TypeSafeStreamSubscription( + controller.stream.listen(null)); + }); + + group("throws a CastError for", () { + test("onData()", () { + expect(() { + // TODO(nweiz): Use the wrapper declared in setUp when sdk#26226 is + // fixed. + controller = new StreamController(); + wrapper = new TypeSafeStreamSubscription( + controller.stream.listen(null)); + + wrapper.onData(expectAsync((_) {}, count: 0)); + controller.add("foo"); + }, throwsZonedCastError); + }); + }); + + group("doesn't throw a CastError for", () { + test("onError()", () { + wrapper.onError(expectAsync((error) { + expect(error, equals("oh no")); + })); + controller.add("foo"); + controller.addError("oh no"); + }); + + test("onDone()", () { + wrapper.onDone(expectAsync(() {})); + controller.add("foo"); + controller.close(); + }); + + test("pause(), resume(), and isPaused", () async { + controller.add("foo"); + + expect(wrapper.isPaused, isFalse); + + wrapper.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + expect(wrapper.isPaused, isTrue); + + wrapper.resume(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + expect(wrapper.isPaused, isFalse); + }); + + test("cancel()", () async { + controller.add("foo"); + + wrapper.cancel(); + await flushMicrotasks(); + expect(isCanceled, isTrue); + }); + + test("asFuture()", () { + expect(wrapper.asFuture(12), completion(equals(12))); + controller.add("foo"); + controller.close(); + }); + }); + }); +} diff --git a/pkgs/async/test/typed_wrapper/stream_test.dart b/pkgs/async/test/typed_wrapper/stream_test.dart new file mode 100644 index 00000000..4936120f --- /dev/null +++ b/pkgs/async/test/typed_wrapper/stream_test.dart @@ -0,0 +1,598 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import "package:async/async.dart"; +import "package:async/src/typed/stream.dart"; +import "package:test/test.dart"; + +import '../utils.dart'; + +void main() { + group("with valid types, forwards", () { + var controller; + var wrapper; + var emptyWrapper; + var singleWrapper; + var errorWrapper; + setUp(() { + controller = new StreamController() + ..add(1)..add(2)..add(3)..add(4)..add(5)..close(); + + // TODO(nweiz): Use public methods when test#414 is fixed and we can run + // this on DDC. + wrapper = new TypeSafeStream(controller.stream); + emptyWrapper = new TypeSafeStream(new Stream.empty()); + singleWrapper = new TypeSafeStream( + new Stream.fromIterable([1])); + errorWrapper = new TypeSafeStream( + new Stream.fromFuture(new Future.error("oh no"))); + }); + + test("first", () { + expect(wrapper.first, completion(equals(1))); + expect(emptyWrapper.first, throwsStateError); + }); + + test("last", () { + expect(wrapper.last, completion(equals(5))); + expect(emptyWrapper.last, throwsStateError); + }); + + test("single", () { + expect(wrapper.single, throwsStateError); + expect(singleWrapper.single, completion(equals(1))); + }); + + test("isBroadcast", () { + expect(wrapper.isBroadcast, isFalse); + var broadcastWrapper = new TypeSafeStream( + new Stream.empty().asBroadcastStream()); + expect(broadcastWrapper.isBroadcast, isTrue); + }); + + test("isEmpty", () { + expect(wrapper.isEmpty, completion(isFalse)); + expect(emptyWrapper.isEmpty, completion(isTrue)); + }); + + test("length", () { + expect(wrapper.length, completion(equals(5))); + expect(emptyWrapper.length, completion(equals(0))); + }); + + group("asBroadcastStream()", () { + test("with no parameters", () { + var broadcast = wrapper.asBroadcastStream(); + expect(broadcast.toList(), completion(equals([1, 2, 3, 4, 5]))); + expect(broadcast.toList(), completion(equals([1, 2, 3, 4, 5]))); + }); + + test("with onListen", () { + var broadcast = wrapper.asBroadcastStream( + onListen: expectAsync((subscription) { + expect(subscription, new isInstanceOf>()); + subscription.pause(); + })); + + broadcast.listen(null); + expect(controller.isPaused, isTrue); + }); + + test("with onCancel", () { + var broadcast = wrapper.asBroadcastStream( + onCancel: expectAsync((subscription) { + expect(subscription, new isInstanceOf>()); + subscription.pause(); + })); + + broadcast.listen(null).cancel(); + expect(controller.isPaused, isTrue); + }); + }); + + test("asyncExpand()", () { + expect( + wrapper.asyncExpand((i) => new Stream.fromIterable([i, i])).toList(), + completion(equals([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]))); + }); + + test("asyncMap()", () { + expect(wrapper.asyncMap((i) => new Future.value(i * 2)).toList(), + completion(equals([2, 4, 6, 8, 10]))); + }); + + group("distinct()", () { + test("without equals", () { + expect(wrapper.distinct().toList(), + completion(equals([1, 2, 3, 4, 5]))); + + expect( + new TypeSafeStream( + new Stream.fromIterable([1, 1, 2, 2, 3, 3])) + .distinct().toList(), + completion(equals([1, 2, 3]))); + }); + + test("with equals", () { + expect(wrapper.distinct((i1, i2) => (i1 ~/ 2 == i2 ~/ 2)).toList(), + completion(equals([1, 2, 4]))); + }); + }); + + group("drain()", () { + test("without a value", () { + expect(wrapper.drain(), completes); + expect(() => wrapper.drain(), throwsStateError); + }); + + test("with a value", () { + expect(wrapper.drain(12), completion(equals(12))); + }); + }); + + test("expand()", () { + expect( + wrapper.expand((i) => [i, i]).toList(), + completion(equals([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]))); + }); + + group("firstWhere()", () { + test("finding a value", () { + expect(wrapper.firstWhere((i) => i > 3), completion(equals(4))); + }); + + test("finding no value", () { + expect(wrapper.firstWhere((i) => i > 5), throwsStateError); + }); + + test("with a default value", () { + expect(wrapper.firstWhere((i) => i > 5, defaultValue: () => "value"), + completion(equals("value"))); + }); + }); + + group("lastWhere()", () { + test("finding a value", () { + expect(wrapper.lastWhere((i) => i < 3), completion(equals(2))); + }); + + test("finding no value", () { + expect(wrapper.lastWhere((i) => i > 5), throwsStateError); + }); + + test("with a default value", () { + expect(wrapper.lastWhere((i) => i > 5, defaultValue: () => "value"), + completion(equals("value"))); + }); + }); + + group("singleWhere()", () { + test("finding a single value", () { + expect(wrapper.singleWhere((i) => i == 3), completion(equals(3))); + }); + + test("finding no value", () { + expect(wrapper.singleWhere((i) => i == 6), throwsStateError); + }); + + test("finding multiple values", () { + expect(wrapper.singleWhere((i) => i.isOdd), throwsStateError); + }); + }); + + test("fold()", () { + expect(wrapper.fold("foo", (previous, i) => previous + i.toString()), + completion(equals("foo12345"))); + }); + + test("forEach()", () async { + emptyWrapper.forEach(expectAsync((_) {}, count: 0)); + + var results = []; + await wrapper.forEach(results.add); + expect(results, equals([1, 2, 3, 4, 5])); + }); + + group("handleError()", () { + test("without a test", () { + expect(errorWrapper.handleError(expectAsync((error) { + expect(error, equals("oh no")); + })).toList(), completion(isEmpty)); + }); + + test("with a matching test", () { + expect(errorWrapper.handleError(expectAsync((error) { + expect(error, equals("oh no")); + }), test: expectAsync((error) { + expect(error, equals("oh no")); + return true; + })).toList(), completion(isEmpty)); + }); + + test("with a matching test", () { + expect(errorWrapper.handleError(expectAsync((_) {}, count: 0), + test: expectAsync((error) { + expect(error, equals("oh no")); + return false; + })).toList(), throwsA("oh no")); + }); + }); + + group("listen()", () { + test("with a callback", () { + var subscription; + subscription = wrapper.listen(expectAsync((data) { + expect(data, equals(1)); + + subscription.onData(expectAsync((data) { + expect(data, equals(2)); + subscription.cancel(); + })); + })); + }); + + test("with a null callback", () { + expect(wrapper.listen(null).asFuture(), completes); + }); + }); + + test("map()", () { + expect(wrapper.map((i) => i * 2).toList(), + completion(equals([2, 4, 6, 8, 10]))); + }); + + test("pipe()", () { + var consumer = new StreamController(); + expect(wrapper.pipe(consumer), completes); + expect(consumer.stream.toList(), completion(equals([1, 2, 3, 4, 5]))); + }); + + test("reduce()", () { + expect(wrapper.reduce((value, i) => value + i), completion(equals(15))); + expect(emptyWrapper.reduce((value, i) => value + i), throwsStateError); + }); + + test("skipWhile()", () { + expect(wrapper.skipWhile((i) => i < 3).toList(), + completion(equals([3, 4, 5]))); + }); + + test("takeWhile()", () { + expect(wrapper.takeWhile((i) => i < 3).toList(), + completion(equals([1, 2]))); + }); + + test("toSet()", () { + expect(wrapper.toSet(), completion(unorderedEquals([1, 2, 3, 4, 5]))); + expect( + new TypeSafeStream( + new Stream.fromIterable([1, 1, 2, 2, 3, 3])) + .toSet(), + completion(unorderedEquals([1, 2, 3]))); + }); + + test("transform()", () { + var transformer = new StreamTransformer.fromHandlers( + handleData: (data, sink) { + sink.add(data.toString()); + }); + + expect(wrapper.transform(transformer).toList(), + completion(equals(["1", "2", "3", "4", "5"]))); + }); + + test("where()", () { + expect(wrapper.where((i) => i.isOdd).toList(), + completion(equals([1, 3, 5]))); + }); + + group("any()", () { + test("with matches", () { + expect(wrapper.any((i) => i > 3), completion(isTrue)); + }); + + test("without matches", () { + expect(wrapper.any((i) => i > 5), completion(isFalse)); + }); + }); + + group("every()", () { + test("with all matches", () { + expect(wrapper.every((i) => i < 6), completion(isTrue)); + }); + + test("with some non-matches", () { + expect(wrapper.every((i) => i > 3), completion(isFalse)); + }); + }); + + group("skip()", () { + test("with a valid index", () { + expect(wrapper.skip(3).toList(), completion(equals([4, 5]))); + }); + + test("with a longer index than length", () { + expect(wrapper.skip(6).toList(), completion(isEmpty)); + }); + + test("with a negative index", () { + expect(() => wrapper.skip(-1), throwsArgumentError); + }); + }); + + group("take()", () { + test("with a valid index", () { + expect(wrapper.take(3).toList(), completion(equals([1, 2, 3]))); + }); + + test("with a longer index than length", () { + expect(wrapper.take(6).toList(), completion(equals([1, 2, 3, 4, 5]))); + }); + + test("with a negative index", () { + expect(wrapper.take(-1).toList(), completion(isEmpty)); + }); + }); + + group("elementAt()", () { + test("with a valid index", () { + expect(wrapper.elementAt(3), completion(equals(4))); + }); + + test("with too high an index", () { + expect(wrapper.elementAt(6), throwsRangeError); + }); + + test("with a negative index", () { + expect(wrapper.elementAt(-1), throwsArgumentError); + }); + }); + + group("contains()", () { + test("with an element", () { + expect(wrapper.contains(2), completion(isTrue)); + }); + + test("with a non-element", () { + expect(wrapper.contains(6), completion(isFalse)); + }); + + test("with a non-element of a different type", () { + expect(wrapper.contains("foo"), completion(isFalse)); + }); + }); + + group("join()", () { + test("without a separator", () { + expect(wrapper.join(), completion(equals("12345"))); + }); + + test("with a separator", () { + expect(wrapper.join(" "), completion(equals("1 2 3 4 5"))); + }); + }); + + test("toString()", () { + expect(wrapper.toString(), contains("Stream")); + }); + }); + + group("with invalid types", () { + var wrapper; + var singleWrapper; + setUp(() { + wrapper = new TypeSafeStream( + new Stream.fromIterable(["foo", "bar", "baz"])); + singleWrapper = new TypeSafeStream( + new Stream.fromIterable(["foo"])); + }); + + group("throws a CastError for", () { + test("first", () { + expect(wrapper.first, throwsCastError); + }); + + test("last", () { + expect(wrapper.last, throwsCastError); + }); + + test("single", () { + expect(singleWrapper.single, throwsCastError); + }); + + test("asBroadcastStream()", () { + var broadcast = wrapper.asBroadcastStream(); + expect(broadcast.first, throwsCastError); + }); + + test("asyncExpand()", () { + expect(wrapper.asyncExpand(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("asyncMap()", () { + expect(wrapper.asyncMap(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + group("distinct()", () { + test("without equals", () { + expect(wrapper.distinct().first, throwsCastError); + }); + + test("with equals", () { + expect(wrapper.distinct(expectAsync((_, __) {}, count: 0)).first, + throwsCastError); + }); + }); + + test("expand()", () { + expect(wrapper.expand(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("firstWhere()", () { + expect(wrapper.firstWhere(expectAsync((_) {}, count: 0)), + throwsCastError); + }); + + test("lastWhere()", () { + expect(wrapper.lastWhere(expectAsync((_) {}, count: 0)), + throwsCastError); + }); + + test("singleWhere()", () { + expect(wrapper.singleWhere(expectAsync((_) {}, count: 0)), + throwsCastError); + }); + + test("fold()", () { + expect(wrapper.fold("foo", expectAsync((_, __) {}, count: 0)), + throwsCastError); + }); + + test("forEach()", () async { + expect(wrapper.forEach(expectAsync((_) {}, count: 0)), throwsCastError); + }); + + test("handleError()", () { + expect(wrapper.handleError(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("listen()", () { + expect(() => wrapper.take(1).listen(expectAsync((_) {}, count: 0)), + throwsZonedCastError); + }); + + test("map()", () { + expect(wrapper.map(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("reduce()", () { + expect(wrapper.reduce(expectAsync((_, __) {}, count: 0)), + throwsCastError); + }); + + test("skipWhile()", () { + expect(wrapper.skipWhile(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("takeWhile()", () { + expect(wrapper.takeWhile(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("toList()", () async { + var list = await wrapper.toList(); + expect(() => list.first, throwsCastError); + }, skip: "Re-enable this when test can run DDC (test#414)."); + + test("toSet()", () async { + var asSet = await wrapper.toSet(); + expect(() => asSet.first, throwsCastError); + }, skip: "Re-enable this when test can run DDC (test#414)."); + + test("where()", () { + expect(wrapper.where(expectAsync((_) {}, count: 0)).first, + throwsCastError); + }); + + test("any()", () { + expect(wrapper.any(expectAsync((_) {}, count: 0)), throwsCastError); + }); + + test("every()", () { + expect(wrapper.every(expectAsync((_) {}, count: 0)), throwsCastError); + }); + + test("skip()", () { + expect(wrapper.skip(1).first, throwsCastError); + }); + + test("take()", () { + expect(wrapper.take(1).first, throwsCastError); + }); + + test("elementAt()", () { + expect(wrapper.elementAt(1), throwsCastError); + }); + }); + + group("doesn't throw a CastError for", () { + test("single", () { + expect(wrapper.single, throwsStateError); + }); + + test("length", () { + expect(wrapper.length, completion(equals(3))); + }); + + test("isBroadcast", () { + expect(wrapper.isBroadcast, isFalse); + }); + + test("isEmpty", () { + expect(wrapper.isEmpty, completion(isFalse)); + }); + + group("drain()", () { + test("without a value", () { + expect(wrapper.drain(), completes); + expect(() => wrapper.drain(), throwsStateError); + }); + + test("with a value", () { + expect(wrapper.drain(12), completion(equals(12))); + }); + }); + + test("skip()", () { + expect(() => wrapper.skip(-1), throwsArgumentError); + }); + + group("elementAt()", () { + test("with too high an index", () { + expect(wrapper.elementAt(6), throwsRangeError); + }); + + test("with a negative index", () { + expect(wrapper.elementAt(-1), throwsArgumentError); + }); + }); + + group("contains()", () { + test("with an element", () { + expect(wrapper.contains("foo"), completion(isTrue)); + }); + + test("with a non-element", () { + expect(wrapper.contains("qux"), completion(isFalse)); + }); + + test("with a non-element of a different type", () { + expect(wrapper.contains(1), completion(isFalse)); + }); + }); + + group("join()", () { + test("without a separator", () { + expect(wrapper.join(), completion(equals("foobarbaz"))); + }); + + test("with a separator", () { + expect(wrapper.join(" "), completion(equals("foo bar baz"))); + }); + }); + + test("toString()", () { + expect(wrapper.toString(), contains("Stream")); + }); + }); + }); +} diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index c32ea2c1..7b65bcb7 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -16,6 +16,29 @@ Future flushMicrotasks() => new Future.delayed(Duration.ZERO); /// Returns a function that fails the test if it is ever called. unreachable(String name) => ([a, b]) => fail("Unreachable: $name"); +// TODO(nweiz): Use the version of this in test when test#418 is fixed. +/// A matcher that runs a callback in its own zone and asserts that that zone +/// emits an error that matches [matcher]. +Matcher throwsZoned(matcher) => predicate((callback) { + var firstError = true; + runZoned(callback, onError: expectAsync((error, stackTrace) { + if (firstError) { + expect(error, matcher); + firstError = false; + } else { + registerException(error, stackTrace); + } + }, max: -1)); + return true; +}); + +/// A matcher that runs a callback in its own zone and asserts that that zone +/// emits a [CastError]. +final throwsZonedCastError = throwsZoned(new isInstanceOf()); + +/// A matcher that matches a callback or future that throws a [CastError]. +final throwsCastError = throwsA(new isInstanceOf()); + /// A badly behaved stream which throws if it's ever listened to. /// /// Can be used to test cases where a stream should not be used. From 7042af4a88ffd861eba3f90664fb577e026b7c8e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 9 May 2016 12:31:46 -0700 Subject: [PATCH 063/260] Add APIs for asserting the types of transformers. I thought about adding these to Delegating*Transformer classes, but those delegates don't really make any sense at all on their own, so I found other places instead. R=lrn@google.com Review URL: https://codereview.chromium.org//1947923003 . --- pkgs/async/CHANGELOG.md | 10 +++++++ pkgs/async/lib/async.dart | 1 + .../lib/src/stream_sink_transformer.dart | 14 +++++++++ .../src/stream_sink_transformer/typed.dart | 20 +++++++++++++ .../lib/src/typed_stream_transformer.dart | 30 +++++++++++++++++++ pkgs/async/pubspec.yaml | 2 +- 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/stream_sink_transformer/typed.dart create mode 100644 pkgs/async/lib/src/typed_stream_transformer.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index b18365fc..de22933e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,13 @@ +## 1.11.0 + +* Add a `typedStreamTransformer()` function. This wraps an untyped + `StreamTransformer` with the correct type parameters, and asserts the types of + events as they're emitted from the transformed stream. + +* Add a `StreamSinkTransformer.typed()` static method. This wraps an untyped + `StreamSinkTransformer` with the correct type parameters, and asserts the + types of arguments passed in to the resulting sink. + ## 1.10.0 * Add `DelegatingFuture.typed()`, `DelegatingStreamSubscription.typed()`, diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 70553410..82189860 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -30,3 +30,4 @@ export "src/stream_sink_transformer.dart"; export "src/stream_splitter.dart"; export "src/stream_zip.dart"; export "src/subscription_stream.dart"; +export "src/typed_stream_transformer.dart"; diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index 6cde04a7..d40bc4b7 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'stream_sink_transformer/handler_transformer.dart'; import 'stream_sink_transformer/stream_transformer_wrapper.dart'; +import 'stream_sink_transformer/typed.dart'; /// A [StreamSinkTransformer] transforms the events being passed to a sink. /// @@ -45,4 +46,17 @@ abstract class StreamSinkTransformer { /// Creates a new sink. When events are passed to the returned sink, it will /// transform them and pass the transformed versions to [sink]. StreamSink bind(StreamSink sink); + + /// Creates a wrapper that coerces the type of [transformer]. + /// + /// This soundly converts a [StreamSinkTransformer] to a + /// `StreamSinkTransformer`, regardless of its original generic type. + /// This means that calls to [StreamSink.add] on the returned sink may throw a + /// [CastError] if the argument type doesn't match the reified type of the + /// sink. + static StreamSinkTransformer/**/ typed/**/( + StreamSinkTransformer transformer) => + transformer is StreamSinkTransformer/**/ + ? transformer + : new TypeSafeStreamSinkTransformer(transformer); } diff --git a/pkgs/async/lib/src/stream_sink_transformer/typed.dart b/pkgs/async/lib/src/stream_sink_transformer/typed.dart new file mode 100644 index 00000000..303bc08d --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_transformer/typed.dart @@ -0,0 +1,20 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import '../delegate/stream_sink.dart'; +import '../stream_sink_transformer.dart'; + +/// A wrapper that coerces the generic type of the sink returned by an inner +/// transformer to `S`. +class TypeSafeStreamSinkTransformer + implements StreamSinkTransformer { + final StreamSinkTransformer _inner; + + TypeSafeStreamSinkTransformer(this._inner); + + StreamSink bind(StreamSink sink) => + DelegatingStreamSink.typed(_inner.bind(sink)); +} diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart new file mode 100644 index 00000000..2fb20c46 --- /dev/null +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -0,0 +1,30 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'delegate/stream.dart'; + +/// Creates a wrapper that coerces the type of [transformer]. +/// +/// This soundly converts a [StreamTransformer] to a `StreamTransformer`, +/// regardless of its original generic type, by asserting that the events +/// emitted by the transformed stream are instances of `T` whenever they're +/// provided. If they're not, the stream throws a [CastError]. +StreamTransformer/**/ typedStreamTransformer/**/( + StreamTransformer transformer) => + transformer is StreamTransformer/**/ + ? transformer + : new _TypeSafeStreamTransformer(transformer); + +/// A wrapper that coerces the type of the stream returned by an inner +/// transformer. +class _TypeSafeStreamTransformer implements StreamTransformer { + final StreamTransformer _inner; + + _TypeSafeStreamTransformer(this._inner); + + Stream bind(Stream stream) => + DelegatingStream.typed(_inner.bind(stream)); +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0859e291..c6843a48 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.10.0 +version: 1.11.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 53a4122dc17f7f7bf2955f498f55c066be87db91 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 19 Jul 2016 14:52:40 -0700 Subject: [PATCH 064/260] Fix Dart 1.17 strong-mode warnings. R=kevmoo@google.com Review URL: https://codereview.chromium.org//2161283002 . --- pkgs/async/CHANGELOG.md | 4 +++ pkgs/async/lib/src/delegate/future.dart | 2 +- .../lib/src/delegate/stream_subscription.dart | 3 +- pkgs/async/lib/src/typed/future.dart | 4 +-- pkgs/async/lib/src/typed/stream.dart | 18 ++++++------ .../lib/src/typed/stream_subscription.dart | 4 ++- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/restartable_timer_test.dart | 4 +-- pkgs/async/test/result_test.dart | 8 +++--- pkgs/async/test/stream_completer_test.dart | 4 +-- pkgs/async/test/stream_queue_test.dart | 28 +++++++++---------- pkgs/async/test/stream_splitter_test.dart | 2 +- pkgs/async/test/stream_zip_test.dart | 2 +- pkgs/async/test/subscription_stream_test.dart | 4 +-- .../async/test/typed_wrapper/future_test.dart | 18 +++--------- .../stream_subscription_test.dart | 1 - .../async/test/typed_wrapper/stream_test.dart | 3 +- pkgs/async/test/utils.dart | 5 +++- 18 files changed, 58 insertions(+), 58 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index de22933e..1d47eafb 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.11.1 + +* Fix new strong-mode warnings introduced in Dart 1.17.0. + ## 1.11.0 * Add a `typedStreamTransformer()` function. This wraps an untyped diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 3f7ea72d..8b81076b 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -24,7 +24,7 @@ class DelegatingFuture implements Future { Stream asStream() => _future.asStream(); - Future catchError(Function onError, {bool test(Object error)}) => + Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index b86e91f9..5fa7b017 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -51,7 +51,8 @@ class DelegatingStreamSubscription implements StreamSubscription { Future cancel() => _source.cancel(); - Future asFuture([futureValue]) => _source.asFuture(futureValue); + Future/**/ asFuture/**/([/*=E*/ futureValue]) => + _source.asFuture(futureValue); bool get isPaused => _source.isPaused; } diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart index a269593d..f53ec5f9 100644 --- a/pkgs/async/lib/src/typed/future.dart +++ b/pkgs/async/lib/src/typed/future.dart @@ -11,8 +11,8 @@ class TypeSafeFuture implements Future { Stream asStream() => _future.then((value) => value as T).asStream(); - Future catchError(Function onError, {bool test(Object error)}) => - _future.catchError(onError, test: test); + Future catchError(Function onError, {bool test(Object error)}) async => + new TypeSafeFuture(_future.catchError(onError, test: test)); Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => _future.then((value) => onValue(value as T), onError: onError); diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart index 3db9f699..afa4462d 100644 --- a/pkgs/async/lib/src/typed/stream.dart +++ b/pkgs/async/lib/src/typed/stream.dart @@ -8,6 +8,7 @@ import 'package:collection/collection.dart'; import '../utils.dart'; import 'stream_subscription.dart'; +import '../delegate/event_sink.dart'; class TypeSafeStream implements Stream { final Stream _stream; @@ -36,20 +37,19 @@ class TypeSafeStream implements Stream { onCancel(new TypeSafeStreamSubscription(subscription)))); } - // TODO(nweiz): Give this a generic parameter when sdk#26125 is fixed. - Stream asyncExpand(Stream convert(T event)) => + Stream/**/ asyncExpand/**/(Stream/**/ convert(T event)) => _stream.asyncExpand(_validateType(convert)); - // TODO(nweiz): Give this a generic parameter when sdk#26125 is fixed. - Stream asyncMap(convert(T event)) => _stream.asyncMap(_validateType(convert)); + Stream/**/ asyncMap/**/(convert(T event)) => + _stream.asyncMap(_validateType(convert)); Stream distinct([bool equals(T previous, T next)]) => new TypeSafeStream(_stream.distinct(equals == null ? null : (previous, next) => equals(previous as T, next as T))); - // TODO(nweiz): Give this a generic parameter when sdk#26125 is fixed. - Future drain([futureValue]) => _stream.drain(futureValue); + Future/**/ drain/**/([/*=E*/ futureValue]) => + _stream.drain(futureValue); Stream/**/ expand/**/(Iterable/**/ convert(T value)) => _stream.expand(_validateType(convert)); @@ -99,8 +99,10 @@ class TypeSafeStream implements Stream { Stream takeWhile(bool test(T element)) => new TypeSafeStream(_stream.takeWhile(_validateType(test))); - Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => - _stream.timeout(timeLimit, onTimeout: onTimeout); + Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => + new TypeSafeStream(_stream.timeout( + timeLimit, + onTimeout: (sink) => onTimeout(DelegatingEventSink.typed(sink)))); Future> toList() async => DelegatingList.typed/**/(await _stream.toList()); diff --git a/pkgs/async/lib/src/typed/stream_subscription.dart b/pkgs/async/lib/src/typed/stream_subscription.dart index 800a4bad..2cd4ddfd 100644 --- a/pkgs/async/lib/src/typed/stream_subscription.dart +++ b/pkgs/async/lib/src/typed/stream_subscription.dart @@ -32,5 +32,7 @@ class TypeSafeStreamSubscription implements StreamSubscription { } Future cancel() => _subscription.cancel(); - Future asFuture([futureValue]) => _subscription.asFuture(futureValue); + + Future/**/ asFuture/**/([/*=E*/ futureValue]) => + _subscription.asFuture(futureValue); } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index c6843a48..9ea1832e 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.11.0 +version: 1.11.1 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index 7dbbc2e4..f1c53fc8 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -10,7 +10,7 @@ main() { test("runs the callback once the duration has elapsed", () { new FakeAsync().run((async) { var fired = false; - var timer = new RestartableTimer(new Duration(seconds: 5), () { + new RestartableTimer(new Duration(seconds: 5), () { fired = true; }); @@ -99,7 +99,7 @@ main() { test("only runs the callback once if the timer isn't reset", () { new FakeAsync().run((async) { - var timer = new RestartableTimer( + new RestartableTimer( new Duration(seconds: 5), expectAsync(() {}, count: 1)); async.elapse(new Duration(seconds: 10)); diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 7af3fcf3..1c5f335b 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -57,7 +57,7 @@ void main() { test("complete with value", () { Result result = new ValueResult(42); - Completer c = new Completer(); + var c = new Completer(); c.future.then(expectAsync((int v) { expect(v, equals(42)); }), onError: (e, s) { fail("Unexpected error"); }); result.complete(c); @@ -65,7 +65,7 @@ void main() { test("complete with error", () { Result result = new ErrorResult("BAD", stack); - Completer c = new Completer(); + var c = new Completer(); c.future.then((bool v) { fail("Unexpected value $v"); }, onError: expectAsync((e, s) { expect(e, equals("BAD")); @@ -75,7 +75,7 @@ void main() { }); test("add sink value", () { - Result result = new ValueResult(42); + var result = new ValueResult(42); EventSink sink = new TestSink( onData: expectAsync((v) { expect(v, equals(42)); }) ); @@ -189,7 +189,7 @@ void main() { test("release stream", () { StreamController> c = new StreamController>(); Stream stream = Result.releaseStream(c.stream); - List events = [new Result.value(42), + var events = [new Result.value(42), new Result.error("BAD", stack), new Result.value(37)]; // Expect the data events, and an extra error event. diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 76d81d1c..6eb5138c 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -130,7 +130,7 @@ main() { test("source stream isn't listened to until completer stream is", () async { var completer = new StreamCompleter(); - var controller; + StreamController controller; controller = new StreamController(onListen: () { scheduleMicrotask(controller.close); }); @@ -281,7 +281,7 @@ main() { var completer = new StreamCompleter(); var controller = new StreamController(); var subscription = completer.stream.listen(null); - var lastEvent = 0; + Object lastEvent = 0; subscription.onData((value) => lastEvent = value); subscription.onError((value) => lastEvent = "$value"); subscription.onDone(() => lastEvent = -1); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 228ba8ad..5140890f 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -12,7 +12,7 @@ import "utils.dart"; main() { group("source stream", () { test("is listened to on first request, paused between requests", () async { - var controller = new StreamController(); + var controller = new StreamController(); var events = new StreamQueue(controller.stream); await flushMicrotasks(); expect(controller.hasListener, isFalse); @@ -288,7 +288,7 @@ main() { test("forwards to underlying stream", () async { var cancel = new Completer(); - var controller = new StreamController(onCancel: () => cancel.future); + var controller = new StreamController(onCancel: () => cancel.future); var events = new StreamQueue(controller.stream); expect(controller.hasListener, isFalse); var next = events.next; @@ -347,14 +347,14 @@ main() { test("cancels underlying subscription when called before any event", () async { var cancelFuture = new Future.value(42); - var controller = new StreamController(onCancel: () => cancelFuture); + var controller = new StreamController(onCancel: () => cancelFuture); var events = new StreamQueue(controller.stream); expect(await events.cancel(), 42); }); test("cancels underlying subscription, returns result", () async { var cancelFuture = new Future.value(42); - var controller = new StreamController(onCancel: () => cancelFuture); + var controller = new StreamController(onCancel: () => cancelFuture); var events = new StreamQueue(controller.stream); controller.add(1); expect(await events.next, 1); @@ -373,7 +373,7 @@ main() { }); test("cancels the underlying subscription immediately", () async { - var controller = new StreamController(); + var controller = new StreamController(); controller.add(1); var events = new StreamQueue(controller.stream); @@ -387,7 +387,7 @@ main() { test("cancels the underlying subscription when called before any event", () async { var cancelFuture = new Future.value(42); - var controller = new StreamController(onCancel: () => cancelFuture); + var controller = new StreamController(onCancel: () => cancelFuture); var events = new StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); @@ -404,7 +404,7 @@ main() { test("returns the result of closing the underlying subscription", () async { - var controller = new StreamController( + var controller = new StreamController( onCancel: () => new Future.value(42)); var events = new StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); @@ -413,7 +413,7 @@ main() { test("listens and then cancels a stream that hasn't been listened to yet", () async { var wasListened = false; - var controller = new StreamController( + var controller = new StreamController( onListen: () => wasListened = true); var events = new StreamQueue(controller.stream); expect(wasListened, isFalse); @@ -448,7 +448,7 @@ main() { test("true when enqueued", () async { var events = new StreamQueue(createStream()); - var values = []; + var values = []; for (int i = 1; i <= 3; i++) { events.next.then(values.add); } @@ -459,7 +459,7 @@ main() { test("false when enqueued", () async { var events = new StreamQueue(createStream()); - var values = []; + var values = []; for (int i = 1; i <= 4; i++) { events.next.then(values.add); } @@ -469,7 +469,7 @@ main() { }); test("true when data event", () async { - var controller = new StreamController(); + var controller = new StreamController(); var events = new StreamQueue(controller.stream); var hasNext; @@ -483,7 +483,7 @@ main() { }); test("true when error event", () async { - var controller = new StreamController(); + var controller = new StreamController(); var events = new StreamQueue(controller.stream); var hasNext; @@ -525,7 +525,7 @@ main() { test("- next after true, enqueued", () async { var events = new StreamQueue(createStream()); - var responses = []; + var responses = []; events.next.then(responses.add); events.hasNext.then(responses.add); events.next.then(responses.add); @@ -683,7 +683,7 @@ Stream createStream() async* { } Stream createErrorStream() { - StreamController controller = new StreamController(); + var controller = new StreamController(); () async { controller.add(1); await flushMicrotasks(); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index ffd0c87a..077ba226 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -8,7 +8,7 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; main() { - var controller; + StreamController controller; var splitter; setUp(() { controller = new StreamController(); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 47f3d8dc..a00d8bd6 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -36,7 +36,7 @@ int ctr = 0; main() { // Test that zipping [streams] gives the results iterated by [expectedData]. - testZip(Iterable streams, Iterable expectedData) { + testZip(Iterable streams, Iterable expectedData) { List data = []; Stream zip = new StreamZip(streams); zip.listen(data.add, onDone: expectAsync(() { diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 0d1870b2..699babee 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -22,7 +22,7 @@ main() { var stream = createStream(); var skips = 0; var completer = new Completer(); - var subscription; + StreamSubscription subscription; subscription = stream.listen((value) { ++skips; expect(value, skips); @@ -168,7 +168,7 @@ Stream createErrorStream([Completer onCancel]) async* { await flushMicrotasks(); yield 2; await flushMicrotasks(); - yield* new Future.error("To err is divine!").asStream(); + yield* new Future.error("To err is divine!").asStream(); await flushMicrotasks(); yield 4; await flushMicrotasks(); diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart index 6601ac1a..b3ff0a70 100644 --- a/pkgs/async/test/typed_wrapper/future_test.dart +++ b/pkgs/async/test/typed_wrapper/future_test.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import "package:async/async.dart"; import "package:async/src/typed/future.dart"; import "package:test/test.dart"; @@ -13,7 +12,7 @@ import '../utils.dart'; void main() { group("with valid types, forwards", () { var wrapper; - var errorWrapper; + TypeSafeFuture errorWrapper; setUp(() { wrapper = new TypeSafeFuture(new Future.value(12)); @@ -35,11 +34,11 @@ void main() { expect(errorWrapper.catchError(expectAsync((error) { expect(error, equals("oh no")); - return "value"; + return 42; }), test: expectAsync((error) { expect(error, equals("oh no")); return true; - })), completion(equals("value"))); + })), completion(equals(42))); }); test("then()", () { @@ -71,7 +70,7 @@ void main() { }); group("with invalid types", () { - var wrapper; + TypeSafeFuture wrapper; setUp(() { wrapper = new TypeSafeFuture(new Future.value("foo")); }); @@ -104,14 +103,5 @@ void main() { throwsCastError); }); }); - - group("doesn't throw a CastError for", () { - test("catchError()", () { - // catchError has a Future return type, so even if there's no - // error we don't re-wrap the returned future. - expect(wrapper.catchError(expectAsync((_) {}, count: 0)), - completion(equals("foo"))); - }); - }); }); } diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index 30d38922..ca2ba860 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import "package:async/async.dart"; import "package:async/src/typed/stream_subscription.dart"; import "package:test/test.dart"; diff --git a/pkgs/async/test/typed_wrapper/stream_test.dart b/pkgs/async/test/typed_wrapper/stream_test.dart index 4936120f..05cde3a9 100644 --- a/pkgs/async/test/typed_wrapper/stream_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_test.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import "package:async/async.dart"; import "package:async/src/typed/stream.dart"; import "package:test/test.dart"; @@ -245,7 +244,7 @@ void main() { }); test("pipe()", () { - var consumer = new StreamController(); + var consumer = new StreamController(); expect(wrapper.pipe(consumer), completes); expect(consumer.stream.toList(), completion(equals([1, 2, 3, 4, 5]))); }); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 7b65bcb7..50e106bd 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -11,10 +11,13 @@ import "package:test/test.dart"; /// A zero-millisecond timer should wait until after all microtasks. Future flushMicrotasks() => new Future.delayed(Duration.ZERO); +typedef void OptionalArgAction([a, b]); + /// A generic unreachable callback function. /// /// Returns a function that fails the test if it is ever called. -unreachable(String name) => ([a, b]) => fail("Unreachable: $name"); +OptionalArgAction unreachable(String name) => + ([a, b]) => fail("Unreachable: $name"); // TODO(nweiz): Use the version of this in test when test#418 is fixed. /// A matcher that runs a callback in its own zone and asserts that that zone From 7246811ce32da4858ca5ad7512c6af8e45ecca03 Mon Sep 17 00:00:00 2001 From: Keerti Parthasarathy Date: Thu, 18 Aug 2016 11:02:59 -0700 Subject: [PATCH 065/260] fix strong mode errors for 1.19.0-dev.4.0 BUG= R=jmesserly@google.com Review URL: https://codereview.chromium.org//2250513003 . --- pkgs/async/lib/src/delegate/future.dart | 4 ++-- pkgs/async/lib/src/typed/future.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 8b81076b..08b22e8c 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -27,8 +27,8 @@ class DelegatingFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); - Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => - _future.then(onValue, onError: onError); + Future/**/ then/**/(onValue(T value), {Function onError}) => + _future.then/**/(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart index f53ec5f9..4deb4a9e 100644 --- a/pkgs/async/lib/src/typed/future.dart +++ b/pkgs/async/lib/src/typed/future.dart @@ -14,8 +14,8 @@ class TypeSafeFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) async => new TypeSafeFuture(_future.catchError(onError, test: test)); - Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => - _future.then((value) => onValue(value as T), onError: onError); + Future/**/ then/**/(onValue(T value), {Function onError}) => + _future.then/**/((value) => onValue(value as T), onError: onError); Future whenComplete(action()) => new TypeSafeFuture(_future.whenComplete(action)); From 4bf538d8ec127d51c48c2c5793c7a07da0076b1c Mon Sep 17 00:00:00 2001 From: Keerti Parthasarathy Date: Thu, 18 Aug 2016 16:11:56 -0700 Subject: [PATCH 066/260] fix strong mode errors for 1.19.0-dev.4.0 BUG= R=jmesserly@google.com, leafp@google.com, nweiz@google.com Review URL: https://codereview.chromium.org//2250513003 . Committed: https://github.com/dart-lang/async/commit/7246811ce32da4858ca5ad7512c6af8e45ecca03 --- pkgs/async/lib/src/delegate/future.dart | 4 ++-- pkgs/async/lib/src/typed/future.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 08b22e8c..8b81076b 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -27,8 +27,8 @@ class DelegatingFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); - Future/**/ then/**/(onValue(T value), {Function onError}) => - _future.then/**/(onValue, onError: onError); + Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => + _future.then(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart index 4deb4a9e..f53ec5f9 100644 --- a/pkgs/async/lib/src/typed/future.dart +++ b/pkgs/async/lib/src/typed/future.dart @@ -14,8 +14,8 @@ class TypeSafeFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) async => new TypeSafeFuture(_future.catchError(onError, test: test)); - Future/**/ then/**/(onValue(T value), {Function onError}) => - _future.then/**/((value) => onValue(value as T), onError: onError); + Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => + _future.then((value) => onValue(value as T), onError: onError); Future whenComplete(action()) => new TypeSafeFuture(_future.whenComplete(action)); From 2d49e1abf9302c50499355139a0215f5db0bc3ef Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 27 Sep 2016 12:19:43 +0200 Subject: [PATCH 067/260] Don't expect a `null` return from subscription.cancel. R=floitsch@google.com Review URL: https://codereview.chromium.org//2369953004 . --- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_group_test.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 9ea1832e..3981abbf 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.11.1 +version: 1.11.2 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 5078dad4..76b69b95 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -507,7 +507,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { await flushMicrotasks(); controller.add("second"); - expect(streamGroup.remove(controller.stream), isNull); + expect(streamGroup.remove(controller.stream), completion(null)); expect(streamGroup.close(), completes); }); @@ -548,7 +548,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(streamGroup.stream.toList(), completion(isEmpty)); controller.add("first"); - expect(streamGroup.remove(controller.stream), isNull); + expect(streamGroup.remove(controller.stream), completion(null)); controller.add("second"); expect(streamGroup.close(), completes); From a04407491e1d39250b64886c9059e42fcf2ed703 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 21 Oct 2016 16:00:44 -0700 Subject: [PATCH 068/260] Change signature of methods overriding Future.then (dart-lang/async#7) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The signature is: Future.then (((T) → dynamic, {onError: Function}) → Future) Use `dynamic` rather than leave off the type to future proof against --no-implicit-dynamic Constrain SDK to version which includes this signature for Future --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/delegate/future.dart | 2 +- pkgs/async/lib/src/typed/future.dart | 2 +- pkgs/async/pubspec.yaml | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1d47eafb..eafa7b0b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.11.3 + +* Fix strong-mode warning against the signature of Future.then + ## 1.11.1 * Fix new strong-mode warnings introduced in Dart 1.17.0. diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 8b81076b..e30d41d9 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -27,7 +27,7 @@ class DelegatingFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); - Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => + Future/**/ then/**/(dynamic onValue(T value), {Function onError}) => _future.then(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart index f53ec5f9..995f4b5c 100644 --- a/pkgs/async/lib/src/typed/future.dart +++ b/pkgs/async/lib/src/typed/future.dart @@ -14,7 +14,7 @@ class TypeSafeFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) async => new TypeSafeFuture(_future.catchError(onError, test: test)); - Future/**/ then/**/(/*=S*/ onValue(T value), {Function onError}) => + Future/**/ then/**/(dynamic onValue(T value), {Function onError}) => _future.then((value) => onValue(value as T), onError: onError); Future whenComplete(action()) => diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3981abbf..27417472 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.11.2 +version: 1.11.3 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async @@ -10,4 +10,4 @@ dev_dependencies: stack_trace: "^1.0.0" test: "^0.12.0" environment: - sdk: ">=1.12.0 <2.0.0" + sdk: ">=1.19.0 <2.0.0" From ed32aaf522816695bc75e4b5fbd1d17a1eaa6e51 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 5 Jan 2017 14:32:11 -0500 Subject: [PATCH 069/260] Additional strong-mode fixes (down to 0 after this PR) (dart-lang/async#13) * Additional strong-mode fixes * Revert infererred types --- .../async/test/cancelable_operation_test.dart | 24 ++++---- pkgs/async/test/future_group_test.dart | 6 +- pkgs/async/test/lazy_stream_test.dart | 12 ++-- pkgs/async/test/restartable_timer_test.dart | 2 +- pkgs/async/test/result_test.dart | 44 +++++++-------- pkgs/async/test/stream_completer_test.dart | 8 +-- pkgs/async/test/stream_group_test.dart | 4 +- pkgs/async/test/stream_queue_test.dart | 4 +- .../test/stream_sink_completer_test.dart | 8 +-- .../test/stream_sink_transformer_test.dart | 20 +++---- pkgs/async/test/stream_splitter_test.dart | 6 +- pkgs/async/test/stream_zip_test.dart | 6 +- pkgs/async/test/stream_zip_zone_test.dart | 6 +- .../async/test/typed_wrapper/future_test.dart | 24 ++++---- .../stream_subscription_test.dart | 12 ++-- .../async/test/typed_wrapper/stream_test.dart | 56 +++++++++---------- pkgs/async/test/utils.dart | 2 +- 17 files changed, 123 insertions(+), 121 deletions(-) diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 4d8c295e..f30c134c 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -14,7 +14,7 @@ void main() { var completer; setUp(() { completer = new CancelableCompleter( - onCancel: expectAsync(() {}, count: 0)); + onCancel: expectAsync0(() {}, count: 0)); }); test("sends values to the future", () { @@ -107,7 +107,7 @@ void main() { group("when canceled", () { test("causes the future never to fire", () async { var completer = new CancelableCompleter(); - completer.operation.value.whenComplete(expectAsync(() {}, count: 0)); + completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); completer.operation.cancel(); // Give the future plenty of time to fire if it's going to. @@ -119,7 +119,7 @@ void main() { test("fires onCancel", () { var canceled = false; var completer; - completer = new CancelableCompleter(onCancel: expectAsync(() { + completer = new CancelableCompleter(onCancel: expectAsync0(() { expect(completer.isCanceled, isTrue); canceled = true; })); @@ -134,7 +134,7 @@ void main() { }); test("returns the onCancel future each time cancel is called", () { - var completer = new CancelableCompleter(onCancel: expectAsync(() { + var completer = new CancelableCompleter(onCancel: expectAsync0(() { return new Future.value(1); })); expect(completer.operation.cancel(), completion(equals(1))); @@ -143,13 +143,13 @@ void main() { }); test("returns a future even if onCancel doesn't", () { - var completer = new CancelableCompleter(onCancel: expectAsync(() {})); + var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); expect(completer.operation.cancel(), completes); }); test("doesn't call onCancel if the completer has completed", () { var completer = new CancelableCompleter( - onCancel: expectAsync(() {}, count: 0)); + onCancel: expectAsync0(() {}, count: 0)); completer.complete(1); expect(completer.operation.value, completion(equals(1))); expect(completer.operation.cancel(), completes); @@ -157,7 +157,7 @@ void main() { test("does call onCancel if the completer has completed to an unfired " "Future", () { - var completer = new CancelableCompleter(onCancel: expectAsync(() {})); + var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); completer.complete(new Completer().future); expect(completer.operation.cancel(), completes); }); @@ -165,7 +165,7 @@ void main() { test("doesn't call onCancel if the completer has completed to a fired " "Future", () async { var completer = new CancelableCompleter( - onCancel: expectAsync(() {}, count: 0)); + onCancel: expectAsync0(() {}, count: 0)); completer.complete(new Future.value(1)); await completer.operation.value; expect(completer.operation.cancel(), completes); @@ -173,7 +173,7 @@ void main() { test("can be completed once after being canceled", () async { var completer = new CancelableCompleter(); - completer.operation.value.whenComplete(expectAsync(() {}, count: 0)); + completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); await completer.operation.cancel(); completer.complete(1); expect(() => completer.complete(1), throwsStateError); @@ -228,10 +228,10 @@ void main() { }); test("cancels the completer when the subscription is canceled", () { - var completer = new CancelableCompleter(onCancel: expectAsync(() {})); + var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); var sub = completer.operation.asStream() - .listen(expectAsync((_) {}, count: 0)); - completer.operation.value.whenComplete(expectAsync(() {}, count: 0)); + .listen(expectAsync1((_) {}, count: 0)); + completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); sub.cancel(); expect(completer.isCanceled, isTrue); }); diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index 09ea29a0..af04799b 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -187,16 +187,16 @@ void main() { var onIdleDone = false; var futureFired = false; - futureGroup.onIdle.listen(expectAsync((_) { + futureGroup.onIdle.listen(expectAsync1((_) { expect(futureFired, isFalse); idle = true; - }), onDone: expectAsync(() { + }), onDone: expectAsync0(() { expect(idle, isTrue); expect(futureFired, isFalse); onIdleDone = true; })); - futureGroup.future.then(expectAsync((_) { + futureGroup.future.then(expectAsync1((_) { expect(idle, isTrue); expect(onIdleDone, isTrue); futureFired = true; diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index 63f2a197..8da13665 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -16,7 +16,7 @@ main() { test("calls the callback when the stream is listened", () async { var callbackCalled = false; - var stream = new LazyStream(expectAsync(() { + var stream = new LazyStream(expectAsync0(() { callbackCalled = true; return new Stream.empty(); })); @@ -30,7 +30,7 @@ main() { test("calls the callback when the stream is listened", () async { var callbackCalled = false; - var stream = new LazyStream(expectAsync(() { + var stream = new LazyStream(expectAsync0(() { callbackCalled = true; return new Stream.empty(); })); @@ -44,7 +44,7 @@ main() { test("forwards to a synchronously-provided stream", () async { var controller = new StreamController(); - var stream = new LazyStream(expectAsync(() => controller.stream)); + var stream = new LazyStream(expectAsync0(() => controller.stream)); var events = []; stream.listen(events.add); @@ -66,7 +66,7 @@ main() { test("forwards to an asynchronously-provided stream", () async { var controller = new StreamController(); - var stream = new LazyStream(expectAsync(() async => controller.stream)); + var stream = new LazyStream(expectAsync0(() async => controller.stream)); var events = []; stream.listen(events.add); @@ -87,7 +87,7 @@ main() { }); test("a lazy stream can't be listened to multiple times", () { - var stream = new LazyStream(expectAsync(() => new Stream.empty())); + var stream = new LazyStream(expectAsync0(() => new Stream.empty())); expect(stream.isBroadcast, isFalse); stream.listen(null); @@ -97,7 +97,7 @@ main() { test("a lazy stream can't be listened to from within its callback", () { var stream; - stream = new LazyStream(expectAsync(() { + stream = new LazyStream(expectAsync0(() { expect(() => stream.listen(null), throwsStateError); return new Stream.empty(); })); diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index f1c53fc8..b612f6e8 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -101,7 +101,7 @@ main() { new FakeAsync().run((async) { new RestartableTimer( new Duration(seconds: 5), - expectAsync(() {}, count: 1)); + expectAsync0(() {}, count: 1)); async.elapse(new Duration(seconds: 10)); }); }); diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 1c5f335b..21e251c6 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -58,7 +58,7 @@ void main() { test("complete with value", () { Result result = new ValueResult(42); var c = new Completer(); - c.future.then(expectAsync((int v) { expect(v, equals(42)); }), + c.future.then(expectAsync1((int v) { expect(v, equals(42)); }), onError: (e, s) { fail("Unexpected error"); }); result.complete(c); }); @@ -67,7 +67,7 @@ void main() { Result result = new ErrorResult("BAD", stack); var c = new Completer(); c.future.then((bool v) { fail("Unexpected value $v"); }, - onError: expectAsync((e, s) { + onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); })); @@ -77,7 +77,7 @@ void main() { test("add sink value", () { var result = new ValueResult(42); EventSink sink = new TestSink( - onData: expectAsync((v) { expect(v, equals(42)); }) + onData: expectAsync1((v) { expect(v, equals(42)); }) ); result.addTo(sink); }); @@ -85,7 +85,7 @@ void main() { test("add sink error", () { Result result = new ErrorResult("BAD", stack); EventSink sink = new TestSink( - onError: expectAsync((e, s) { + onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); }) @@ -95,14 +95,14 @@ void main() { test("value as future", () { Result result = new ValueResult(42); - result.asFuture.then(expectAsync((int v) { expect(v, equals(42)); }), + result.asFuture.then(expectAsync1((int v) { expect(v, equals(42)); }), onError: (e, s) { fail("Unexpected error"); }); }); test("error as future", () { Result result = new ErrorResult("BAD", stack); result.asFuture.then((bool v) { fail("Unexpected value $v"); }, - onError: expectAsync((e, s) { + onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); })); @@ -110,7 +110,7 @@ void main() { test("capture future value", () { Future value = new Future.value(42); - Result.capture(value).then(expectAsync((Result result) { + Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isTrue); expect(result.isError, isFalse); ValueResult value = result.asValue; @@ -122,7 +122,7 @@ void main() { test("capture future error", () { Future value = new Future.error("BAD", stack); - Result.capture(value).then(expectAsync((Result result) { + Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isFalse); expect(result.isError, isTrue); ErrorResult error = result.asError; @@ -136,7 +136,7 @@ void main() { test("release future value", () { Future> future = new Future>.value(new Result.value(42)); - Result.release(future).then(expectAsync((v) { + Result.release(future).then(expectAsync1((v) { expect(v, equals(42)); }), onError: (e, s) { fail("Unexpected error: $e"); @@ -149,7 +149,7 @@ void main() { new Future>.value(new Result.error("BAD", stack)); Result.release(future).then((v) { fail("Unexpected value: $v"); - }, onError: expectAsync((e, s) { + }, onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); })); @@ -160,7 +160,7 @@ void main() { Future> future = new Future>.error("BAD", stack); Result.release(future).then((v) { fail("Unexpected value: $v"); - }, onError: expectAsync((e, s) { + }, onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); })); @@ -176,9 +176,9 @@ void main() { expect(expectedList.isEmpty, isFalse); expectResult(actual, expectedList.removeFirst()); } - stream.listen(expectAsync(listener, count: 3), + stream.listen(expectAsync1(listener, count: 3), onError: (e, s) { fail("Unexpected error: $e"); }, - onDone: expectAsync((){}), + onDone: expectAsync0((){}), cancelOnError: true); c.add(42); c.addError("BAD", stack); @@ -211,9 +211,9 @@ void main() { expect(stackTrace, same(expected.asError.stackTrace)); } - stream.listen(expectAsync(dataListener, count: 2), - onError: expectAsync(errorListener, count: 2), - onDone: expectAsync((){})); + stream.listen(expectAsync1(dataListener, count: 2), + onError: expectAsync2(errorListener, count: 2), + onDone: expectAsync0((){})); for (Result result in events) { c.add(result); // Result value or error in data line. } @@ -224,8 +224,8 @@ void main() { test("release stream cancel on error", () { StreamController> c = new StreamController>(); Stream stream = Result.releaseStream(c.stream); - stream.listen(expectAsync((v) { expect(v, equals(42)); }), - onError: expectAsync((e, s) { + stream.listen(expectAsync1((v) { expect(v, equals(42)); }), + onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); }), @@ -259,7 +259,7 @@ void main() { }); test("handle unary", () { - var result = new Result.error("error", stack); + ErrorResult result = new Result.error("error", stack); bool called = false; result.handle((error) { called = true; @@ -269,7 +269,7 @@ void main() { }); test("handle binary", () { - var result = new Result.error("error", stack); + ErrorResult result = new Result.error("error", stack); bool called = false; result.handle((error, stackTrace) { called = true; @@ -280,7 +280,7 @@ void main() { }); test("handle unary and binary", () { - var result = new Result.error("error", stack); + ErrorResult result = new Result.error("error", stack); bool called = false; result.handle((error, [stackTrace]) { called = true; @@ -291,7 +291,7 @@ void main() { }); test("handle neither unary nor binary", () { - var result = new Result.error("error", stack); + ErrorResult result = new Result.error("error", stack); expect(() => result.handle(() => fail("unreachable")), throws); expect(() => result.handle((a, b, c) => fail("unreachable")), diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 6eb5138c..dea73d78 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -343,10 +343,10 @@ main() { var completer = new StreamCompleter(); completer.stream.listen( unreachable("data"), - onError: expectAsync((error, stackTrace) { + onError: expectAsync2((error, stackTrace) { expect(error, equals("oh no")); }), - onDone: expectAsync(() {})); + onDone: expectAsync0(() {})); completer.setError("oh no"); }); @@ -359,10 +359,10 @@ main() { completer.stream.listen( unreachable("data"), - onError: expectAsync((error, stackTrace) { + onError: expectAsync2((error, stackTrace) { expect(error, equals("oh no")); }), - onDone: expectAsync(() {})); + onDone: expectAsync0(() {})); }); }); } diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 76b69b95..078dc3c6 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -211,7 +211,7 @@ main() { var canceled = false; var controller = new StreamController(onListen: () { listened = true; - }, onCancel: expectAsync(() { + }, onCancel: expectAsync0(() { expect(listened, isTrue); canceled = true; })); @@ -357,7 +357,7 @@ main() { var subscription = streamGroup.stream.listen(null); var controller = new StreamController( - onCancel: expectAsync(() {}, count: 0)); + onCancel: expectAsync0(() {}, count: 0)); streamGroup.add(controller.stream); await flushMicrotasks(); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 5140890f..54d470db 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -150,7 +150,7 @@ main() { var skip4 = events.skip(1); var index = 0; // Check that futures complete in order. - sequence(expectedValue, sequenceIndex) => (value) { + Func1Required sequence(expectedValue, sequenceIndex) => (value) { expect(value, expectedValue); expect(index, sequenceIndex); index++; @@ -672,6 +672,8 @@ main() { }); } +typedef T Func1Required(T value); + Stream createStream() async* { yield 1; await flushMicrotasks(); diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 82393fcd..c7ce1ea6 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -73,7 +73,7 @@ main() { completer.setDestinationSink(sink); var closeCompleted = false; - completer.sink.close().then(expectAsync((_) { + completer.sink.close().then(expectAsync1((_) { closeCompleted = true; })); @@ -174,7 +174,7 @@ main() { test("the future from the inner close() is returned", () async { var closeCompleted = false; - completer.sink.close().then(expectAsync((_) { + completer.sink.close().then(expectAsync1((_) { closeCompleted = true; })); await flushMicrotasks(); @@ -247,7 +247,7 @@ main() { group("fromFuture()", () { test("with a successful completion", () async { - var futureCompleter = new Completer(); + var futureCompleter = new Completer(); var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); sink.add(1); sink.add(2); @@ -264,7 +264,7 @@ main() { }); test("with an error", () async { - var futureCompleter = new Completer(); + var futureCompleter = new Completer(); var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); expect(sink.done, throwsA("oh no")); futureCompleter.completeError("oh no"); diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index 70e7e6cb..6be9f9ee 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -24,7 +24,7 @@ void main() { var sink = transformer.bind(controller.sink); var results = []; - controller.stream.listen(results.add, onDone: expectAsync(() { + controller.stream.listen(results.add, onDone: expectAsync0(() { expect(results, equals([2, 4, 6])); })); @@ -38,16 +38,16 @@ void main() { var transformer = new StreamSinkTransformer.fromStreamTransformer( new StreamTransformer.fromHandlers( handleError: (i, stackTrace, sink) { - sink.addError(i * 2, stackTrace); + sink.addError((i as num) * 2, stackTrace); })); var sink = transformer.bind(controller.sink); var results = []; - controller.stream.listen(expectAsync((_) {}, count: 0), + controller.stream.listen(expectAsync1((_) {}, count: 0), onError: (error, stackTrace) { results.add(error); }, - onDone: expectAsync(() { + onDone: expectAsync0(() { expect(results, equals([2, 4, 6])); })); @@ -67,7 +67,7 @@ void main() { var sink = transformer.bind(controller.sink); var results = []; - controller.stream.listen(results.add, onDone: expectAsync(() { + controller.stream.listen(results.add, onDone: expectAsync0(() { expect(results, equals([1])); })); @@ -126,7 +126,7 @@ void main() { var sink = transformer.bind(controller.sink); var results = []; - controller.stream.listen(results.add, onDone: expectAsync(() { + controller.stream.listen(results.add, onDone: expectAsync0(() { expect(results, equals([2, 4, 6])); })); @@ -139,16 +139,16 @@ void main() { test("transforms error events", () { var transformer = new StreamSinkTransformer.fromHandlers( handleError: (i, stackTrace, sink) { - sink.addError(i * 2, stackTrace); + sink.addError((i as num) * 2, stackTrace); }); var sink = transformer.bind(controller.sink); var results = []; - controller.stream.listen(expectAsync((_) {}, count: 0), + controller.stream.listen(expectAsync1((_) {}, count: 0), onError: (error, stackTrace) { results.add(error); }, - onDone: expectAsync(() { + onDone: expectAsync0(() { expect(results, equals([2, 4, 6])); })); @@ -167,7 +167,7 @@ void main() { var sink = transformer.bind(controller.sink); var results = []; - controller.stream.listen(results.add, onDone: expectAsync(() { + controller.stream.listen(results.add, onDone: expectAsync0(() { expect(results, equals([1])); })); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 077ba226..c562305e 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -47,15 +47,15 @@ main() { controller.close(); var count = 0; - branch.listen(expectAsync((value) { + branch.listen(expectAsync1((value) { expect(count, anyOf(0, 2)); expect(value, equals(count + 1)); count++; - }, count: 2), onError: expectAsync((error) { + }, count: 2), onError: expectAsync1((error) { expect(count, equals(1)); expect(error, equals("error")); count++; - }), onDone: expectAsync(() { + }), onDone: expectAsync0(() { expect(count, equals(3)); })); }); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index a00d8bd6..958d1f22 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -39,7 +39,7 @@ main() { testZip(Iterable streams, Iterable expectedData) { List data = []; Stream zip = new StreamZip(streams); - zip.listen(data.add, onDone: expectAsync(() { + zip.listen(data.add, onDone: expectAsync0(() { expect(data, equals(expectedData)); })); } @@ -174,7 +174,7 @@ main() { sc2p--; }); - var done = expectAsync((){ + var done = expectAsync0((){ expect(sc1p, equals(1)); expect(sc2p, equals(0)); }); // Call to complete test. @@ -220,7 +220,7 @@ main() { var sz = new StreamZip([s1, s2]); int ctr = 0; var sub; - sub = sz.listen(expectAsync((v) { + sub = sz.listen(expectAsync1((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); if (ctr == 1) { sub.pause(new Future.delayed(const Duration(milliseconds: 25))); diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index 47957a94..af4d94a4 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -33,15 +33,15 @@ void testStream(String name, StreamController controller, Stream stream) { runZoned(() { Zone newZone1 = Zone.current; StreamSubscription sub; - sub = stream.listen(expectAsync((v) { + sub = stream.listen(expectAsync1((v) { expect(v, 42); expect(Zone.current, newZone1); outer.run(() { - sub.onData(expectAsync((v) { + sub.onData(expectAsync1((v) { expect(v, 37); expect(Zone.current, newZone1); runZoned(() { - sub.onData(expectAsync((v) { + sub.onData(expectAsync1((v) { expect(v, 87); expect(Zone.current, newZone1); })); diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart index b3ff0a70..b928e054 100644 --- a/pkgs/async/test/typed_wrapper/future_test.dart +++ b/pkgs/async/test/typed_wrapper/future_test.dart @@ -28,14 +28,14 @@ void main() { test("catchError()", () { expect( - wrapper.catchError(expectAsync((_) {}, count: 0), - test: expectAsync((_) {}, count: 0)), + wrapper.catchError(expectAsync1((_) {}, count: 0), + test: expectAsync1((_) {}, count: 0)), completion(equals(12))); - expect(errorWrapper.catchError(expectAsync((error) { + expect(errorWrapper.catchError(expectAsync1((error) { expect(error, equals("oh no")); return 42; - }), test: expectAsync((error) { + }), test: expectAsync1((error) { expect(error, equals("oh no")); return true; })), completion(equals(42))); @@ -44,13 +44,13 @@ void main() { test("then()", () { expect(wrapper.then((value) => value.toString()), completion(equals("12"))); - expect(errorWrapper.then(expectAsync((_) {}, count: 0)), + expect(errorWrapper.then(expectAsync1((_) {}, count: 0)), throwsA("oh no")); }); test("whenComplete()", () { - expect(wrapper.whenComplete(expectAsync(() {})), completion(equals(12))); - expect(errorWrapper.whenComplete(expectAsync(() {})), throwsA("oh no")); + expect(wrapper.whenComplete(expectAsync0(() {})), completion(equals(12))); + expect(errorWrapper.whenComplete(expectAsync0(() {})), throwsA("oh no")); }); test("timeout()", () { @@ -64,7 +64,7 @@ void main() { expect( new TypeSafeFuture(new Completer().future) - .timeout(Duration.ZERO, onTimeout: expectAsync(() => 15)), + .timeout(Duration.ZERO, onTimeout: expectAsync0(() => 15)), completion(equals(15))); }); }); @@ -82,13 +82,13 @@ void main() { test("then()", () { expect( - wrapper.then(expectAsync((_) {}, count: 0), - onError: expectAsync((_) {}, count: 0)), + wrapper.then(expectAsync1((_) {}, count: 0), + onError: expectAsync1((_) {}, count: 0)), throwsCastError); }); test("whenComplete()", () { - expect(wrapper.whenComplete(expectAsync(() {})).then((_) {}), + expect(wrapper.whenComplete(expectAsync0(() {})).then((_) {}), throwsCastError); }); @@ -98,7 +98,7 @@ void main() { expect( new TypeSafeFuture(new Completer().future) - .timeout(Duration.ZERO, onTimeout: expectAsync(() => "foo")) + .timeout(Duration.ZERO, onTimeout: expectAsync0(() => "foo")) .then((_) {}), throwsCastError); }); diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index ca2ba860..07a8bd29 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -23,21 +23,21 @@ void main() { }); test("onData()", () { - wrapper.onData(expectAsync((data) { + wrapper.onData(expectAsync1((data) { expect(data, equals(1)); })); controller.add(1); }); test("onError()", () { - wrapper.onError(expectAsync((error) { + wrapper.onError(expectAsync1((error) { expect(error, equals("oh no")); })); controller.addError("oh no"); }); test("onDone()", () { - wrapper.onDone(expectAsync(() {})); + wrapper.onDone(expectAsync0(() {})); controller.close(); }); @@ -88,7 +88,7 @@ void main() { wrapper = new TypeSafeStreamSubscription( controller.stream.listen(null)); - wrapper.onData(expectAsync((_) {}, count: 0)); + wrapper.onData(expectAsync1((_) {}, count: 0)); controller.add("foo"); }, throwsZonedCastError); }); @@ -96,7 +96,7 @@ void main() { group("doesn't throw a CastError for", () { test("onError()", () { - wrapper.onError(expectAsync((error) { + wrapper.onError(expectAsync1((error) { expect(error, equals("oh no")); })); controller.add("foo"); @@ -104,7 +104,7 @@ void main() { }); test("onDone()", () { - wrapper.onDone(expectAsync(() {})); + wrapper.onDone(expectAsync0(() {})); controller.add("foo"); controller.close(); }); diff --git a/pkgs/async/test/typed_wrapper/stream_test.dart b/pkgs/async/test/typed_wrapper/stream_test.dart index 05cde3a9..12a130a3 100644 --- a/pkgs/async/test/typed_wrapper/stream_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_test.dart @@ -71,7 +71,7 @@ void main() { test("with onListen", () { var broadcast = wrapper.asBroadcastStream( - onListen: expectAsync((subscription) { + onListen: expectAsync1((subscription) { expect(subscription, new isInstanceOf>()); subscription.pause(); })); @@ -82,7 +82,7 @@ void main() { test("with onCancel", () { var broadcast = wrapper.asBroadcastStream( - onCancel: expectAsync((subscription) { + onCancel: expectAsync1((subscription) { expect(subscription, new isInstanceOf>()); subscription.pause(); })); @@ -188,7 +188,7 @@ void main() { }); test("forEach()", () async { - emptyWrapper.forEach(expectAsync((_) {}, count: 0)); + emptyWrapper.forEach(expectAsync1((_) {}, count: 0)); var results = []; await wrapper.forEach(results.add); @@ -197,23 +197,23 @@ void main() { group("handleError()", () { test("without a test", () { - expect(errorWrapper.handleError(expectAsync((error) { + expect(errorWrapper.handleError(expectAsync1((error) { expect(error, equals("oh no")); })).toList(), completion(isEmpty)); }); test("with a matching test", () { - expect(errorWrapper.handleError(expectAsync((error) { + expect(errorWrapper.handleError(expectAsync1((error) { expect(error, equals("oh no")); - }), test: expectAsync((error) { + }), test: expectAsync1((error) { expect(error, equals("oh no")); return true; })).toList(), completion(isEmpty)); }); test("with a matching test", () { - expect(errorWrapper.handleError(expectAsync((_) {}, count: 0), - test: expectAsync((error) { + expect(errorWrapper.handleError(expectAsync1((_) {}, count: 0), + test: expectAsync1((error) { expect(error, equals("oh no")); return false; })).toList(), throwsA("oh no")); @@ -223,10 +223,10 @@ void main() { group("listen()", () { test("with a callback", () { var subscription; - subscription = wrapper.listen(expectAsync((data) { + subscription = wrapper.listen(expectAsync1((data) { expect(data, equals(1)); - subscription.onData(expectAsync((data) { + subscription.onData(expectAsync1((data) { expect(data, equals(2)); subscription.cancel(); })); @@ -408,12 +408,12 @@ void main() { }); test("asyncExpand()", () { - expect(wrapper.asyncExpand(expectAsync((_) {}, count: 0)).first, + expect(wrapper.asyncExpand(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("asyncMap()", () { - expect(wrapper.asyncMap(expectAsync((_) {}, count: 0)).first, + expect(wrapper.asyncMap(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); @@ -423,67 +423,67 @@ void main() { }); test("with equals", () { - expect(wrapper.distinct(expectAsync((_, __) {}, count: 0)).first, + expect(wrapper.distinct(expectAsync2((_, __) {}, count: 0)).first, throwsCastError); }); }); test("expand()", () { - expect(wrapper.expand(expectAsync((_) {}, count: 0)).first, + expect(wrapper.expand(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("firstWhere()", () { - expect(wrapper.firstWhere(expectAsync((_) {}, count: 0)), + expect(wrapper.firstWhere(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("lastWhere()", () { - expect(wrapper.lastWhere(expectAsync((_) {}, count: 0)), + expect(wrapper.lastWhere(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("singleWhere()", () { - expect(wrapper.singleWhere(expectAsync((_) {}, count: 0)), + expect(wrapper.singleWhere(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("fold()", () { - expect(wrapper.fold("foo", expectAsync((_, __) {}, count: 0)), + expect(wrapper.fold("foo", expectAsync2((_, __) {}, count: 0)), throwsCastError); }); test("forEach()", () async { - expect(wrapper.forEach(expectAsync((_) {}, count: 0)), throwsCastError); + expect(wrapper.forEach(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("handleError()", () { - expect(wrapper.handleError(expectAsync((_) {}, count: 0)).first, + expect(wrapper.handleError(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("listen()", () { - expect(() => wrapper.take(1).listen(expectAsync((_) {}, count: 0)), + expect(() => wrapper.take(1).listen(expectAsync1((_) {}, count: 0)), throwsZonedCastError); }); test("map()", () { - expect(wrapper.map(expectAsync((_) {}, count: 0)).first, + expect(wrapper.map(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("reduce()", () { - expect(wrapper.reduce(expectAsync((_, __) {}, count: 0)), + expect(wrapper.reduce(expectAsync2((_, __) {}, count: 0)), throwsCastError); }); test("skipWhile()", () { - expect(wrapper.skipWhile(expectAsync((_) {}, count: 0)).first, + expect(wrapper.skipWhile(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("takeWhile()", () { - expect(wrapper.takeWhile(expectAsync((_) {}, count: 0)).first, + expect(wrapper.takeWhile(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); @@ -498,16 +498,16 @@ void main() { }, skip: "Re-enable this when test can run DDC (test#414)."); test("where()", () { - expect(wrapper.where(expectAsync((_) {}, count: 0)).first, + expect(wrapper.where(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("any()", () { - expect(wrapper.any(expectAsync((_) {}, count: 0)), throwsCastError); + expect(wrapper.any(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("every()", () { - expect(wrapper.every(expectAsync((_) {}, count: 0)), throwsCastError); + expect(wrapper.every(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("skip()", () { diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 50e106bd..8499eb24 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -24,7 +24,7 @@ OptionalArgAction unreachable(String name) => /// emits an error that matches [matcher]. Matcher throwsZoned(matcher) => predicate((callback) { var firstError = true; - runZoned(callback, onError: expectAsync((error, stackTrace) { + runZoned(callback, onError: expectAsync2((error, stackTrace) { if (firstError) { expect(error, matcher); firstError = false; From a0a575552dfde359773efa2ff8537a1847ac0899 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 5 Jan 2017 19:29:58 -0500 Subject: [PATCH 070/260] Add AsyncCache (dart-lang/async#12) --- pkgs/async/CHANGELOG.md | 5 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/async_cache.dart | 104 +++++++++++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/async_cache_test.dart | 154 ++++++++++++++++++++++++++ 5 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/async_cache.dart create mode 100644 pkgs/async/test/async_cache_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index eafa7b0b..378a162d 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.12.0 + +* Add an `AsyncCache` class that caches asynchronous operations for a period of + time. + ## 1.11.3 * Fix strong-mode warning against the signature of Future.then diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 82189860..2526ae5f 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -2,6 +2,7 @@ // 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. +export "src/async_cache.dart"; export "src/async_memoizer.dart"; export "src/cancelable_operation.dart"; export "src/delegate/event_sink.dart"; diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart new file mode 100644 index 00000000..96e1a50e --- /dev/null +++ b/pkgs/async/lib/src/async_cache.dart @@ -0,0 +1,104 @@ +// 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. + +import 'dart:async'; + +import 'package:async/async.dart'; + +/// Runs asynchronous functions and caches the result for a period of time. +/// +/// This class exists to cover the pattern of having potentially expensive code +/// such as file I/O, network access, or isolate computation that's unlikely to +/// change quickly run fewer times. For example: +/// +/// ```dart +/// final _usersCache = new AsyncCache>(const Duration(hours: 1)); +/// +/// /// Uses the cache if it exists, otherwise calls the closure: +/// Future> get onlineUsers => _usersCache.fetch(() { +/// // Actually fetch online users here. +/// }); +/// ``` +/// +/// This class's timing can be mocked using [`fake_async`][fake_async]. +/// +/// [fake_async]: https://pub.dartlang.org/packages/fake_async +class AsyncCache { + /// How long cached values stay fresh. + final Duration _duration; + + /// Cached results of a previous [fetchStream] call. + StreamSplitter _cachedStreamSplitter; + + /// Cached results of a previous [fetch] call. + Future _cachedValueFuture; + + /// Fires when the cache should be considered stale. + Timer _stale; + + /// Creates a cache that invalidates after an in-flight request is complete. + /// + /// An ephemeral cache guarantees that a callback function will only be + /// executed at most once concurrently. This is useful for requests for which + /// data is updated frequently but stale data is acceptable. + factory AsyncCache.ephemeral() => + new AsyncCache(Duration.ZERO); + + /// Creates a cache that invalidates its contents after [duration] has passed. + /// + /// The [duration] starts counting after the Future returned by [fetch] + /// completes, or after the Stream returned by [fetchStream] emits a done + /// event. + AsyncCache(this._duration); + + /// Returns a cached value from a previous call to [fetch], or runs [callback] + /// to compute a new one. + /// + /// If [fetch] has been called recently enough, returns its previous return + /// value. Otherwise, runs [callback] and returns its new return value. + Future fetch(Future callback()) async { + if (_cachedStreamSplitter != null) { + throw new StateError('Previously used to cache via `fetchStream`'); + } + if (_cachedValueFuture == null) { + _cachedValueFuture = callback(); + await _cachedValueFuture; + _startStaleTimer(); + } + return _cachedValueFuture; + } + + /// Returns a cached stream from a previous call to [fetchStream], or runs + /// [callback] to compute a new stream. + /// + /// If [fetchStream] has been called recently enough, returns a copy of its + /// previous return value. Otherwise, runs [callback] and returns its new + /// return value. + Stream fetchStream(Stream callback()) { + if (_cachedValueFuture != null) { + throw new StateError('Previously used to cache via `fetch`'); + } + if (_cachedStreamSplitter == null) { + _cachedStreamSplitter = new StreamSplitter(callback() + .transform(new StreamTransformer.fromHandlers(handleDone: (sink) { + _startStaleTimer(); + sink.close(); + }))); + } + return _cachedStreamSplitter.split(); + } + + /// Removes any cached value. + void invalidate() { + _cachedValueFuture = null; + _cachedStreamSplitter?.close(); + _cachedStreamSplitter = null; + _stale?.cancel(); + _stale = null; + } + + void _startStaleTimer() { + _stale = new Timer(_duration, invalidate); + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 27417472..20ec7339 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.11.3 +version: 1.12.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart new file mode 100644 index 00000000..b81d9f9f --- /dev/null +++ b/pkgs/async/test/async_cache_test.dart @@ -0,0 +1,154 @@ +// 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:fake_async/fake_async.dart'; +import 'package:test/test.dart'; + +void main() { + AsyncCache cache; + + setUp(() { + // Create a cache that is fresh for an hour. + cache = new AsyncCache(const Duration(hours: 1)); + }); + + test('should fetch via a callback when no cache exists', () async { + expect(await cache.fetch(() async => 'Expensive'), 'Expensive'); + }); + + test('should not fetch via callback when a cache exists', () async { + await cache.fetch(() async => 'Expensive'); + expect(await cache.fetch(expectAsync0(() {}, count: 0)), 'Expensive'); + }); + + test('should not fetch via callback when a future is in-flight', () async { + // No actual caching is done, just avoid duplicate requests. + cache = new AsyncCache.ephemeral(); + + var completer = new Completer(); + expect(cache.fetch(() => completer.future), completion('Expensive')); + expect(cache.fetch(expectAsync0(() {}, count: 0)), completion('Expensive')); + await completer.complete('Expensive'); + }); + + test('should fetch via a callback again when cache expires', () { + new FakeAsync().run((fakeAsync) async { + var timesCalled = 0; + call() async => 'Called ${++timesCalled}'; + expect(await cache.fetch(call), 'Called 1'); + expect(await cache.fetch(call), 'Called 1', reason: 'Cache still fresh'); + + fakeAsync.elapse(const Duration(hours: 1) - const Duration(seconds: 1)); + expect(await cache.fetch(call), 'Called 1', reason: 'Cache still fresh'); + + fakeAsync.elapse(const Duration(seconds: 1)); + expect(await cache.fetch(call), 'Called 2'); + expect(await cache.fetch(call), 'Called 2', reason: 'Cache fresh again'); + + fakeAsync.elapse(const Duration(hours: 1)); + expect(await cache.fetch(call), 'Called 3'); + }); + }); + + test('should fetch via a callback when manually invalidated', () async { + var timesCalled = 0; + call() async => 'Called ${++timesCalled}'; + expect(await cache.fetch(call), 'Called 1'); + await cache.invalidate(); + expect(await cache.fetch(call), 'Called 2'); + await cache.invalidate(); + expect(await cache.fetch(call), 'Called 3'); + }); + + test('should fetch a stream via a callback', () async { + expect(await cache.fetchStream(expectAsync0(() { + return new Stream.fromIterable(['1', '2', '3']); + })).toList(), ['1', '2', '3']); + }); + + test('should not fetch stream via callback when a cache exists', () async { + await cache.fetchStream(() async* { + yield '1'; + yield '2'; + yield '3'; + }).toList(); + expect(await cache.fetchStream(expectAsync0(() {}, count: 0)).toList(), + ['1', '2', '3']); + }); + + test('should not fetch stream via callback when request in flight', () async { + // Unlike the above test, we want to verify that we don't make multiple + // calls if a cache is being filled currently, and instead wait for that + // cache to be completed. + var controller = new StreamController(); + Stream call() => controller.stream; + expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); + controller.add('1'); + controller.add('2'); + await new Future.value(); + expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); + controller.add('3'); + await controller.close(); + }); + + test('should fetch stream via a callback again when cache expires', () { + new FakeAsync().run((fakeAsync) async { + var timesCalled = 0; + Stream call() { + return new Stream.fromIterable(['Called ${++timesCalled}']); + } + + expect(await cache.fetchStream(call).toList(), ['Called 1']); + expect(await cache.fetchStream(call).toList(), ['Called 1'], + reason: 'Cache still fresh'); + + fakeAsync.elapse(const Duration(hours: 1) - const Duration(seconds: 1)); + expect(await cache.fetchStream(call).toList(), ['Called 1'], + reason: 'Cache still fresh'); + + fakeAsync.elapse(const Duration(seconds: 1)); + expect(await cache.fetchStream(call).toList(), ['Called 2']); + expect(await cache.fetchStream(call).toList(), ['Called 2'], + reason: 'Cache fresh again'); + + fakeAsync.elapse(const Duration(hours: 1)); + expect(await cache.fetchStream(call).toList(), ['Called 3']); + }); + }); + + test('should fetch via a callback when manually invalidated', () async { + var timesCalled = 0; + Stream call() { + return new Stream.fromIterable(['Called ${++timesCalled}']); + } + + expect(await cache.fetchStream(call).toList(), ['Called 1']); + await cache.invalidate(); + expect(await cache.fetchStream(call).toList(), ['Called 2']); + await cache.invalidate(); + expect(await cache.fetchStream(call).toList(), ['Called 3']); + }); + + test('should cancel a cached stream without affecting others', () async { + Stream call() => new Stream.fromIterable(['1', '2', '3']); + + expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); + + // Listens to the stream for the initial value, then cancels subscription. + expect(await cache.fetchStream(call).first, '1'); + }); + + test('should pause a cached stream without affecting others', () async { + Stream call() => new Stream.fromIterable(['1', '2', '3']); + + StreamSubscription sub; + sub = cache.fetchStream(call).listen(expectAsync1((event) { + if (event == '1') sub.pause(); + })); + expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); + }); +} From effaced2868e5540943b09f6f253990fd784aeed Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 9 Jan 2017 14:39:45 -0800 Subject: [PATCH 071/260] Add StreamQueue.startTransactions(). (dart-lang/async#9) This is important for advanced pull-based stream manipulation. It allows users to express logic of the form "consume the next events if they match this predicate". --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/src/stream_queue.dart | 210 +++++++++++++++++++++++-- pkgs/async/test/stream_queue_test.dart | 173 +++++++++++++++++++- 3 files changed, 374 insertions(+), 12 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 378a162d..e51766e0 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,9 @@ * Add an `AsyncCache` class that caches asynchronous operations for a period of time. +* Add `StreamQueue.startTransaction()` and `StreamQueue.startTransactions()`. + These allow users to conditionally consume events based on their values. + ## 1.11.3 * Fix strong-mode warning against the signature of Future.then diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 4ce9e130..051f8b2b 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -5,9 +5,12 @@ import 'dart:async'; import 'dart:collection'; +import 'package:collection/collection.dart'; + import "result.dart"; import "subscription_stream.dart"; import "stream_completer.dart"; +import "stream_splitter.dart"; /// An asynchronous pull-based interface for accessing stream events. /// @@ -86,7 +89,7 @@ abstract class StreamQueue { bool _isClosed = false; /// Queue of events not used by a request yet. - final Queue _eventQueue = new Queue(); + final QueueList _eventQueue = new QueueList(); /// Queue of pending requests. /// @@ -210,6 +213,44 @@ abstract class StreamQueue { throw _failClosed(); } + /// Requests a transaction that can conditionally consume events. + /// + /// The transaction can create copies of this queue at the current position + /// using [StreamQueueTransaction.newQueue]. Each of these queues is + /// independent of one another and of the parent queue. The transaction + /// finishes when one of two methods is called: + /// + /// * [StreamQueueTransaction.commit] updates the parent queue's position to + /// match that of one of the copies. + /// + /// * [StreamQueueTransaction.reject] causes the parent queue to continue as + /// though [startTransaction] hadn't been called. + /// + /// Until the transaction finishes, this queue won't emit any events. + /// + /// ```dart + /// /// Consumes all empty lines from the beginning of [lines]. + /// Future consumeEmptyLines(StreamQueue lines) { + /// while (await lines.hasNext) { + /// var transaction = lines.startTransaction(); + /// var queue = transaction.newQueue(); + /// if ((await queue.next).isNotEmpty) { + /// transaction.reject(); + /// return; + /// } else { + /// transaction.accept(queue); + /// } + /// } + /// } + /// ``` + StreamQueueTransaction startTransaction() { + if (_isClosed) throw _failClosed(); + + var request = new _TransactionRequest(this); + _addRequest(request); + return request.transaction; + } + /// Cancels the underlying event source. /// /// If [immediate] is `false` (the default), the cancel operation waits until @@ -361,7 +402,7 @@ class _StreamQueue extends StreamQueue { } void _ensureListening() { - assert(!_isDone); + if (_isDone) return; if (_subscription == null) { _subscription = _sourceStream.listen( @@ -407,6 +448,125 @@ class _StreamQueue extends StreamQueue { } } +/// A transaction on a [StreamQueue], created by [StreamQueue.startTransaction]. +/// +/// Copies of the parent queue may be created using [newQueue]. Calling [commit] +/// moves the parent queue to a copy's position, and calling [reject] causes it +/// to continue as though [StreamQueue.startTransaction] was never called. +class StreamQueueTransaction { + /// The parent queue on which this transaction is active. + final StreamQueue _parent; + + /// The splitter that produces copies of the parent queue's stream. + final StreamSplitter _splitter; + + /// Queues created using [newQueue]. + final _queues = new Set<_TransactionStreamQueue>(); + + /// Whether [commit] has been called. + var _committed = false; + + /// Whether [reject] has been called. + var _rejected = false; + + StreamQueueTransaction._(this._parent, Stream source) + : _splitter = new StreamSplitter(source); + + /// Creates a new copy of the parent queue. + /// + /// This copy starts at the parent queue's position when + /// [StreamQueue.startTransaction] was called. Its position can be committed + /// to the parent queue using [commit]. + StreamQueue newQueue() { + var queue = new _TransactionStreamQueue(_splitter.split()); + _queues.add(queue); + return queue; + } + + /// Commits a queue created using [newQueue]. + /// + /// The parent queue's position is updated to be the same as [queue]'s. + /// Further requests on all queues created by this transaction, including + /// [queue], will complete as though [cancel] were called with `immediate: + /// true`. + /// + /// Throws a [StateError] if [commit] or [reject] have already been called, or + /// if there are pending requests on [queue]. + void commit(StreamQueue queue) { + _assertActive(); + if (!_queues.contains(queue)) { + throw new ArgumentError("Queue doesn't belong to this transaction."); + } else if (queue._requestQueue.isNotEmpty) { + throw new StateError("A queue with pending requests can't be committed."); + } + _committed = true; + + // Remove all events from the parent queue that were consumed by the + // child queue. + var eventsConsumed = (queue as _TransactionStreamQueue)._eventsReceived - + queue._eventQueue.length; + for (var j = 0; j < eventsConsumed; j++) { + _parent._eventQueue.removeFirst(); + } + + _done(); + } + + /// Rejects this transaction without updating the parent queue. + /// + /// The parent will continue as though [StreamQueue.startTransaction] hadn't + /// been called. Further requests on all queues created by this transaction + /// will complete as though [cancel] were called with `immediate: true`. + /// + /// Throws a [StateError] if [commit] or [reject] have already been called. + void reject() { + _assertActive(); + _rejected = true; + _done(); + } + + // Cancels all [_queues], removes the [_TransactionRequest] from [_parent]'s + // request queue, and runs the next request. + void _done() { + _splitter.close(); + for (var queue in _queues) { + queue._cancel(); + } + + assert((_parent._requestQueue.first as _TransactionRequest) + .transaction == this); + _parent._requestQueue.removeFirst(); + _parent._updateRequests(); + } + + /// Throws a [StateError] if [accept] or [reject] has already been called. + void _assertActive() { + if (_committed) { + throw new StateError("This transaction has already been accepted."); + } else if (_rejected) { + throw new StateError("This transaction has already been rejected."); + } + } +} + +/// A [StreamQueue] that belongs to a [StreamQueueTransaction]. +class _TransactionStreamQueue extends _StreamQueue { + /// The total number of events received by this queue, including events that + /// haven't yet been consumed by requests. + /// + /// This is used to fast-forward the parent queue if this transaction is + /// accepted. + var _eventsReceived = 0; + + _TransactionStreamQueue(Stream sourceStream) : super(sourceStream); + + /// Modifies [StreamQueue._addResult] to count the total number of events that + /// have been passed to this transaction. + void _addResult(Result result) { + _eventsReceived++; + super._addResult(result); + } +} /// Request object that receives events when they arrive, until fulfilled. /// @@ -443,7 +603,7 @@ abstract class _EventRequest { /// If the function returns `false` when the stream has already closed /// ([isDone] is true), then the request must call /// [StreamQueue._updateRequests] itself when it's ready to continue. - bool update(Queue> events, bool isDone); + bool update(QueueList> events, bool isDone); } /// Request for a [StreamQueue.next] call. @@ -458,7 +618,7 @@ class _NextRequest implements _EventRequest { Future get future => _completer.future; - bool update(Queue> events, bool isDone) { + bool update(QueueList> events, bool isDone) { if (events.isNotEmpty) { events.removeFirst().complete(_completer); return true; @@ -491,7 +651,7 @@ class _SkipRequest implements _EventRequest { /// The future completed when the correct number of events have been skipped. Future get future => _completer.future; - bool update(Queue> events, bool isDone) { + bool update(QueueList> events, bool isDone) { while (_eventsToSkip > 0) { if (events.isEmpty) { if (isDone) break; @@ -529,7 +689,7 @@ class _TakeRequest implements _EventRequest { /// The future completed when the correct number of events have been captured. Future> get future => _completer.future; - bool update(Queue> events, bool isDone) { + bool update(QueueList> events, bool isDone) { while (_list.length < _eventsToTake) { if (events.isEmpty) { if (isDone) break; @@ -556,8 +716,6 @@ class _TakeRequest implements _EventRequest { class _CancelRequest implements _EventRequest { /// Completer for the future returned by the `cancel` call. final _completer = new Completer(); - - /// The [StreamQueue] object that has this request queued. /// /// When the event is completed, it needs to cancel the active subscription /// of the `StreamQueue` object, if any. @@ -568,7 +726,7 @@ class _CancelRequest implements _EventRequest { /// The future completed when the cancel request is completed. Future get future => _completer.future; - bool update(Queue> events, bool isDone) { + bool update(QueueList> events, bool isDone) { if (_streamQueue._isDone) { _completer.complete(); } else { @@ -599,7 +757,7 @@ class _RestRequest implements _EventRequest { /// The stream which will contain the remaining events of [_streamQueue]. Stream get stream => _completer.stream; - bool update(Queue> events, bool isDone) { + bool update(QueueList> events, bool isDone) { if (events.isEmpty) { if (_streamQueue._isDone) { _completer.setEmpty(); @@ -632,7 +790,7 @@ class _HasNextRequest implements _EventRequest { Future get future => _completer.future; - bool update(Queue> events, bool isDone) { + bool update(QueueList> events, bool isDone) { if (events.isNotEmpty) { _completer.complete(true); return true; @@ -644,3 +802,33 @@ class _HasNextRequest implements _EventRequest { return false; } } + +/// Request for a [StreamQueue.startTransaction] call. +/// +/// This request isn't complete until the user calls +/// [StreamQueueTransaction.commit] or [StreamQueue.rejectTransaction], at which +/// point it manually removes itself from the request queue and calls +/// [StreamQueue._updateRequests]. +class _TransactionRequest implements _EventRequest { + /// The transaction created by this request. + StreamQueueTransaction get transaction => _transaction; + StreamQueueTransaction _transaction; + + /// The controller that passes events to [transaction]. + final _controller = new StreamController(sync: true); + + /// The number of events passed to [_controller] so far. + var _eventsSent = 0; + + _TransactionRequest(StreamQueue parent) { + _transaction = new StreamQueueTransaction._(parent, _controller.stream); + } + + bool update(QueueList> events, bool isDone) { + while (_eventsSent < events.length) { + events[_eventsSent++].addTo(_controller); + } + if (isDone && !_controller.isClosed) _controller.close(); + return false; + } +} diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 54d470db..d5ab74b3 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -4,7 +4,7 @@ import "dart:async"; -import "package:async/async.dart" show StreamQueue; +import "package:async/async.dart"; import "package:test/test.dart"; import "utils.dart"; @@ -629,6 +629,177 @@ main() { }); }); + group("startTransaction operation produces a transaction that", () { + StreamQueue events; + StreamQueueTransaction transaction; + StreamQueue queue1; + StreamQueue queue2; + setUp(() async { + events = new StreamQueue(createStream()); + expect(await events.next, 1); + transaction = events.startTransaction(); + queue1 = transaction.newQueue(); + queue2 = transaction.newQueue(); + }); + + group("emits queues that", () { + test("independently emit events", () async { + expect(await queue1.next, 2); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(await queue1.next, 3); + expect(await queue1.next, 4); + expect(await queue2.next, 4); + expect(await queue1.hasNext, isFalse); + expect(await queue2.hasNext, isFalse); + }); + + test("queue requests for events", () async { + expect(queue1.next, completion(2)); + expect(queue2.next, completion(2)); + expect(queue2.next, completion(3)); + expect(queue1.next, completion(3)); + expect(queue1.next, completion(4)); + expect(queue2.next, completion(4)); + expect(queue1.hasNext, completion(isFalse)); + expect(queue2.hasNext, completion(isFalse)); + }); + + test("independently emit errors", () async { + events = new StreamQueue(createErrorStream()); + expect(await events.next, 1); + transaction = events.startTransaction(); + queue1 = transaction.newQueue(); + queue2 = transaction.newQueue(); + + expect(queue1.next, completion(2)); + expect(queue2.next, completion(2)); + expect(queue2.next, throwsA("To err is divine!")); + expect(queue1.next, throwsA("To err is divine!")); + expect(queue1.next, completion(4)); + expect(queue2.next, completion(4)); + expect(queue1.hasNext, completion(isFalse)); + expect(queue2.hasNext, completion(isFalse)); + }); + }); + + group("when rejected", () { + test("further original requests use the previous state", () async { + expect(await queue1.next, 2); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + + await flushMicrotasks(); + transaction.reject(); + + expect(await events.next, 2); + expect(await events.next, 3); + expect(await events.next, 4); + expect(await events.hasNext, isFalse); + }); + + test("pending original requests use the previous state", () async { + expect(await queue1.next, 2); + expect(await queue2.next, 2); + expect(await queue2.next, 3); + expect(events.next, completion(2)); + expect(events.next, completion(3)); + expect(events.next, completion(4)); + expect(events.hasNext, completion(isFalse)); + + await flushMicrotasks(); + transaction.reject(); + }); + + test("further child requests act as though the stream was closed", + () async { + expect(await queue1.next, 2); + transaction.reject(); + + expect(await queue1.hasNext, isFalse); + expect(queue1.next, throwsStateError); + }); + + test("pending child requests act as though the stream was closed", + () async { + expect(await queue1.next, 2); + expect(queue1.hasNext, completion(isFalse)); + expect(queue1.next, throwsStateError); + transaction.reject(); + }); + + test("child requests' cancel() may still be called explicitly", () async { + transaction.reject(); + await queue1.cancel(); + }); + + test("calls to commit() or reject() fail", () async { + transaction.reject(); + expect(transaction.reject, throwsStateError); + expect(() => transaction.commit(queue1), throwsStateError); + }); + }); + + group("when committed,", () { + test("further original requests use the committed state", () async { + expect(await queue1.next, 2); + await flushMicrotasks(); + transaction.commit(queue1); + expect(await events.next, 3); + }); + + test("pending original requests use the committed state", () async { + expect(await queue1.next, 2); + expect(events.next, completion(3)); + await flushMicrotasks(); + transaction.commit(queue1); + }); + + test("further child requests act as though the stream was closed", + () async { + expect(await queue2.next, 2); + transaction.commit(queue2); + + expect(await queue1.hasNext, isFalse); + expect(queue1.next, throwsStateError); + }); + + test("pending child requests act as though the stream was closed", + () async { + expect(await queue2.next, 2); + expect(queue1.hasNext, completion(isFalse)); + expect(queue1.next, throwsStateError); + transaction.commit(queue2); + }); + + test("further requests act as though the stream was closed", () async { + expect(await queue1.next, 2); + transaction.commit(queue1); + + expect(await queue1.hasNext, isFalse); + expect(queue1.next, throwsStateError); + }); + + test("cancel() may still be called explicitly", () async { + expect(await queue1.next, 2); + transaction.commit(queue1); + await queue1.cancel(); + }); + + test("throws if there are pending requests", () async { + expect(await queue1.next, 2); + expect(queue1.hasNext, completion(isTrue)); + expect(() => transaction.commit(queue1), throwsStateError); + }); + + test("calls to commit() or reject() fail", () async { + transaction.commit(queue1); + expect(transaction.reject, throwsStateError); + expect(() => transaction.commit(queue1), throwsStateError); + }); + }); + }); + test("all combinations sequential skip/next/take operations", () async { // Takes all combinations of two of next, skip and take, then ends with // doing rest. Each of the first rounds do 10 events of each type, From 243419b899822d9fe5deb7367893d9911b4e9643 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 10 Jan 2017 23:24:18 -0800 Subject: [PATCH 072/260] Add StreamQueue.withTransaction(). --- pkgs/async/lib/src/stream_queue.dart | 48 ++++++++++++++++++++++++-- pkgs/async/test/stream_queue_test.dart | 47 +++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 051f8b2b..caedd8b3 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -228,9 +228,11 @@ abstract class StreamQueue { /// /// Until the transaction finishes, this queue won't emit any events. /// + /// See also [withTransaction] and [cancelable]. + /// /// ```dart /// /// Consumes all empty lines from the beginning of [lines]. - /// Future consumeEmptyLines(StreamQueue lines) { + /// Future consumeEmptyLines(StreamQueue lines) async { /// while (await lines.hasNext) { /// var transaction = lines.startTransaction(); /// var queue = transaction.newQueue(); @@ -238,7 +240,7 @@ abstract class StreamQueue { /// transaction.reject(); /// return; /// } else { - /// transaction.accept(queue); + /// transaction.commit(queue); /// } /// } /// } @@ -251,6 +253,48 @@ abstract class StreamQueue { return request.transaction; } + /// Passes a copy of this queue to [callback], and updates this queue to match + /// the copy's position if [callback] returns `true`. + /// + /// This queue won't emit any events until [callback] returns. If it returns + /// `false`, this queue continues as though [withTransaction] hadn't been + /// called. If it throws an error, this updates this queue to match the copy's + /// position and throws the error from the returned `Future`. + /// + /// Returns the same value as [callback]. + /// + /// See also [startTransaction] and [cancelable]. + /// + /// ```dart + /// /// Consumes all empty lines from the beginning of [lines]. + /// Future consumeEmptyLines(StreamQueue lines) async { + /// while (await lines.hasNext) { + /// // Consume a line if it's empty, otherwise return. + /// if (!await lines.withTransaction( + /// (queue) async => (await queue.next).isEmpty)) { + /// return; + /// } + /// } + /// } + /// ``` + Future withTransaction(Future callback(StreamQueue queue)) { + var transaction = startTransaction(); + + /// Avoid async/await to ensure that [startTransaction] is called + /// synchronously and so ends up in the right place in the request queue. + var queue = transaction.newQueue(); + return callback(queue).then((result) { + if (result) { + transaction.commit(queue); + } else { + transaction.reject(); + } + }, onError: (error) { + transaction.commit(queue); + throw error; + }); + } + /// Cancels the underlying event source. /// /// If [immediate] is `false` (the default), the cancel operation waits until diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index d5ab74b3..7ab82be9 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -800,6 +800,53 @@ main() { }); }); + group("withTransaction operation", () { + StreamQueue events; + setUp(() async { + events = new StreamQueue(createStream()); + expect(await events.next, 1); + }); + + test("passes a copy of the parent queue", () async { + await events.withTransaction(expectAsync1((queue) async { + expect(await queue.next, 2); + expect(await queue.next, 3); + expect(await queue.next, 4); + expect(await queue.hasNext, isFalse); + return true; + })); + }); + + test("the parent queue continues from the child position if it returns " + "true", () async { + await events.withTransaction(expectAsync1((queue) async { + expect(await queue.next, 2); + return true; + })); + + expect(await events.next, 3); + }); + + test("the parent queue continues from its original position if it returns " + "false", () async { + await events.withTransaction(expectAsync1((queue) async { + expect(await queue.next, 2); + return false; + })); + + expect(await events.next, 2); + }); + + test("the parent queue continues from the child position if it throws", () { + expect(events.withTransaction(expectAsync1((queue) async { + expect(await queue.next, 2); + throw "oh no"; + })), throwsA("oh no")); + + expect(events.next, completion(3)); + }); + }); + test("all combinations sequential skip/next/take operations", () async { // Takes all combinations of two of next, skip and take, then ends with // doing rest. Each of the first rounds do 10 events of each type, From 980392ececf146d94fbdfff2a6b1ce3529eeead9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 10 Jan 2017 23:24:35 -0800 Subject: [PATCH 073/260] Add StreamQueue.cancelable(). --- pkgs/async/lib/src/stream_queue.dart | 35 +++++++++++++++++ pkgs/async/test/stream_queue_test.dart | 53 ++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index caedd8b3..f770e16e 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -7,6 +7,7 @@ import 'dart:collection'; import 'package:collection/collection.dart'; +import "cancelable_operation.dart"; import "result.dart"; import "subscription_stream.dart"; import "stream_completer.dart"; @@ -295,6 +296,40 @@ abstract class StreamQueue { }); } + /// Passes a copy of this queue to [callback], and updates this queue to match + /// the copy's position once [callback] completes. + /// + /// If the returned [CancelableOperation] is canceled, this queue instead + /// continues as though [cancelable] hadn't been called. Otherwise, it emits + /// the same value or error as [callback]. + /// + /// See also [startTransaction] and [withTransaction]. + /// + /// ```dart + /// final _stdinQueue = new StreamQueue(stdin); + /// + /// /// Returns an operation that completes when the user sends a line to + /// /// standard input. + /// /// + /// /// If the operation is canceled, stops waiting for user input. + /// CancelableOperation nextStdinLine() => + /// _stdinQueue.cancelable((queue) => queue.next); + /// ``` + CancelableOperation/**/ cancelable/**/( + Future/**/ callback(StreamQueue queue)) { + var transaction = startTransaction(); + var completer = new CancelableCompleter/**/(onCancel: () { + transaction.reject(); + }); + + var queue = transaction.newQueue(); + completer.complete(callback(queue).whenComplete(() { + if (!completer.isCanceled) transaction.commit(queue); + })); + + return completer.operation; + } + /// Cancels the underlying event source. /// /// If [immediate] is `false` (the default), the cancel operation waits until diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 7ab82be9..02a77333 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -847,6 +847,59 @@ main() { }); }); + group("cancelable operation", () { + StreamQueue events; + setUp(() async { + events = new StreamQueue(createStream()); + expect(await events.next, 1); + }); + + test("passes a copy of the parent queue", () async { + await events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + expect(await queue.next, 3); + expect(await queue.next, 4); + expect(await queue.hasNext, isFalse); + })).value; + }); + + test("the parent queue continues from the child position by default", + () async { + await events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + })).value; + + expect(await events.next, 3); + }); + + test("the parent queue continues from the child position if an error is " + "thrown", () async { + expect(events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + throw "oh no"; + })).value, throwsA("oh no")); + + expect(events.next, completion(3)); + }); + + test("the parent queue continues from the original position if canceled", + () async { + var operation = events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + })); + operation.cancel(); + + expect(await events.next, 2); + }); + + test("forwards the value from the callback", () async { + expect(await events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + return "value"; + })).value, "value"); + }); + }); + test("all combinations sequential skip/next/take operations", () async { // Takes all combinations of two of next, skip and take, then ends with // doing rest. Each of the first rounds do 10 events of each type, From bc941dbcd0185687a097c7d32bec578db7fa5b77 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 10 Jan 2017 23:24:41 -0800 Subject: [PATCH 074/260] Add CHANGELOG entries. --- pkgs/async/CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index e51766e0..615e33e6 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,9 +3,12 @@ * Add an `AsyncCache` class that caches asynchronous operations for a period of time. -* Add `StreamQueue.startTransaction()` and `StreamQueue.startTransactions()`. +* Add `StreamQueue.startTransaction()` and `StreamQueue.withTransaction()`. These allow users to conditionally consume events based on their values. +* Add `StreamQueue.cancelable()`, which allows users to easily make a + `CancelableOperation` that can be canceled without affecting the queue. + ## 1.11.3 * Fix strong-mode warning against the signature of Future.then From 5877fa424c390fdcd502daa4ef7f13717dada5a7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 19 Jan 2017 12:46:23 -0800 Subject: [PATCH 075/260] Add a subscriptionTransformer() function. (dart-lang/async#10) --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/async.dart | 1 + .../src/stream_subscription_transformer.dart | 105 +++++++ .../test/subscription_transformer_test.dart | 292 ++++++++++++++++++ 4 files changed, 401 insertions(+) create mode 100644 pkgs/async/lib/src/stream_subscription_transformer.dart create mode 100644 pkgs/async/test/subscription_transformer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 615e33e6..43fcf54f 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -9,6 +9,9 @@ * Add `StreamQueue.cancelable()`, which allows users to easily make a `CancelableOperation` that can be canceled without affecting the queue. +* Add a `subscriptionTransformer()` function to create `StreamTransformer`s that + modify the behavior of subscriptions to a stream. + ## 1.11.3 * Fix strong-mode warning against the signature of Future.then diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 2526ae5f..c11f0b56 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -29,6 +29,7 @@ export "src/stream_queue.dart"; export "src/stream_sink_completer.dart"; export "src/stream_sink_transformer.dart"; export "src/stream_splitter.dart"; +export "src/stream_subscription_transformer.dart"; export "src/stream_zip.dart"; export "src/subscription_stream.dart"; export "src/typed_stream_transformer.dart"; diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart new file mode 100644 index 00000000..060f4664 --- /dev/null +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -0,0 +1,105 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'async_memoizer.dart'; +import 'delegate/stream_subscription.dart'; + +typedef Future _AsyncHandler(StreamSubscription inner); + +typedef void _VoidHandler(StreamSubscription inner); + +/// Creates a [StreamTransformer] that modifies the behavior of subscriptions to +/// a stream. +/// +/// When [StreamSubscription.cancel], [StreamSubscription.pause], or +/// [StreamSubscription.resume] is called, the corresponding handler is invoked. +/// By default, handlers just forward to the underlying subscription. +/// +/// Guarantees that none of the [StreamSubscription] callbacks and none of the +/// callbacks passed to `subscriptionTransformer()` will be invoked once the +/// transformed [StreamSubscription] has been canceled and `handleCancel()` has +/// run. The [handlePause] and [handleResume] are invoked regardless of whether +/// the subscription is paused already or not. +/// +/// In order to preserve [StreamSubscription] guarantees, **all callbacks must +/// synchronously call the corresponding method** on the inner +/// [StreamSubscription]: [handleCancel] must call `cancel()`, [handlePause] +/// must call `pause()`, and [handleResume] must call `resume()`. +StreamTransformer/**/ subscriptionTransformer/**/( + {Future handleCancel(StreamSubscription/**/ inner), + void handlePause(StreamSubscription/**/ inner), + void handleResume(StreamSubscription/**/ inner)}) { + return new StreamTransformer((stream, cancelOnError) { + return new _TransformedSubscription( + stream.listen(null, cancelOnError: cancelOnError), + handleCancel ?? (inner) => inner.cancel(), + handlePause ?? (inner) { + inner.pause(); + }, + handleResume ?? (inner) { + inner.resume(); + }); + }); +} + +/// A [StreamSubscription] wrapper that calls callbacks for subscription +/// methods. +class _TransformedSubscription implements StreamSubscription { + /// The wrapped subscription. + StreamSubscription _inner; + + /// The callback to run when [cancel] is called. + final _AsyncHandler _handleCancel; + + /// The callback to run when [pause] is called. + final _VoidHandler _handlePause; + + /// The callback to run when [resume] is called. + final _VoidHandler _handleResume; + + bool get isPaused => _inner?.isPaused ?? false; + + _TransformedSubscription(this._inner, this._handleCancel, this._handlePause, + this._handleResume); + + void onData(void handleData(T data)) { + _inner?.onData(handleData); + } + + void onError(Function handleError) { + _inner?.onError(handleError); + } + + void onDone(void handleDone()) { + _inner?.onDone(handleDone); + } + + Future cancel() => _cancelMemoizer.runOnce(() { + var inner = _inner; + _inner.onData(null); + _inner.onDone(null); + + // Setting onError to null will cause errors to be top-leveled. + _inner.onError((_, __) {}); + _inner = null; + return _handleCancel(inner); + }); + final _cancelMemoizer = new AsyncMemoizer(); + + void pause([Future resumeFuture]) { + if (_cancelMemoizer.hasRun) return; + if (resumeFuture != null) resumeFuture.whenComplete(resume); + _handlePause(_inner); + } + + void resume() { + if (_cancelMemoizer.hasRun) return; + _handleResume(_inner); + } + + Future/**/ asFuture/**/([/*=E*/ futureValue]) => + _inner?.asFuture(futureValue) ?? new Completer().future; +} diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart new file mode 100644 index 00000000..cdac4f8b --- /dev/null +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -0,0 +1,292 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + group("with no callbacks", () { + test("forwards cancellation", () async { + var isCanceled = false; + var cancelCompleter = new Completer(); + var controller = new StreamController(onCancel: expectAsync0(() { + isCanceled = true; + return cancelCompleter.future; + })); + var subscription = controller.stream + .transform(subscriptionTransformer()) + .listen(expectAsync1((_) {}, count: 0)); + + var cancelFired = false; + subscription.cancel().then(expectAsync1((_) { + cancelFired = true; + })); + + await flushMicrotasks(); + expect(isCanceled, isTrue); + expect(cancelFired, isFalse); + + cancelCompleter.complete(); + await flushMicrotasks(); + expect(cancelFired, isTrue); + + // This shouldn't call the onCancel callback again. + expect(subscription.cancel(), completes); + }); + + test("forwards pausing and resuming", () async { + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer()) + .listen(expectAsync1((_) {}, count: 0)); + + subscription.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + subscription.pause(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + subscription.resume(); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + subscription.resume(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + }); + + test("forwards pausing with a resume future", () async { + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer()) + .listen(expectAsync1((_) {}, count: 0)); + + var completer = new Completer(); + subscription.pause(completer.future); + await flushMicrotasks(); + expect(controller.isPaused, isTrue); + + completer.complete(); + await flushMicrotasks(); + expect(controller.isPaused, isFalse); + }); + }); + + group("with a cancel callback", () { + test("invokes the callback when the subscription is canceled", () async { + var isCanceled = false; + var callbackInvoked = false; + var controller = new StreamController(onCancel: expectAsync0(() { + isCanceled = true; + })); + var subscription = controller.stream + .transform(subscriptionTransformer( + handleCancel: expectAsync1((inner) { + callbackInvoked = true; + inner.cancel(); + }))) + .listen(expectAsync1((_) {}, count: 0)); + + await flushMicrotasks(); + expect(callbackInvoked, isFalse); + expect(isCanceled, isFalse); + + subscription.cancel(); + await flushMicrotasks(); + expect(callbackInvoked, isTrue); + expect(isCanceled, isTrue); + }); + + test("invokes the callback once and caches its result", () async { + var completer = new Completer(); + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer( + handleCancel: expectAsync1((inner) => completer.future))) + .listen(expectAsync1((_) {}, count: 0)); + + var cancelFired1 = false; + subscription.cancel().then(expectAsync1((_) { + cancelFired1 = true; + })); + + var cancelFired2 = false; + subscription.cancel().then(expectAsync1((_) { + cancelFired2 = true; + })); + + await flushMicrotasks(); + expect(cancelFired1, isFalse); + expect(cancelFired2, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(cancelFired1, isTrue); + expect(cancelFired2, isTrue); + }); + }); + + group("with a pause callback", () { + test("invokes the callback when pause is called", () async { + var pauseCount = 0; + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer(handlePause: expectAsync1((inner) { + pauseCount++; + inner.pause(); + }, count: 3))) + .listen(expectAsync1((_) {}, count: 0)); + + await flushMicrotasks(); + expect(pauseCount, equals(0)); + + subscription.pause(); + await flushMicrotasks(); + expect(pauseCount, equals(1)); + + subscription.pause(); + await flushMicrotasks(); + expect(pauseCount, equals(2)); + + subscription.resume(); + subscription.resume(); + await flushMicrotasks(); + expect(pauseCount, equals(2)); + + subscription.pause(); + await flushMicrotasks(); + expect(pauseCount, equals(3)); + }); + + test("doesn't invoke the callback when the subscription has been canceled", + () async { + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer( + handlePause: expectAsync1((_) {}, count: 0))) + .listen(expectAsync1((_) {}, count: 0)); + + subscription.cancel(); + subscription.pause(); + subscription.pause(); + subscription.pause(); + }); + }); + + group("with a resume callback", () { + test("invokes the callback when resume is called", () async { + var resumeCount = 0; + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer( + handleResume: expectAsync1((inner) { + resumeCount++; + inner.resume(); + }, count: 3))) + .listen(expectAsync1((_) {}, count: 0)); + + await flushMicrotasks(); + expect(resumeCount, equals(0)); + + subscription.resume(); + await flushMicrotasks(); + expect(resumeCount, equals(1)); + + subscription.pause(); + subscription.pause(); + await flushMicrotasks(); + expect(resumeCount, equals(1)); + + subscription.resume(); + await flushMicrotasks(); + expect(resumeCount, equals(2)); + + subscription.resume(); + await flushMicrotasks(); + expect(resumeCount, equals(3)); + }); + + test("invokes the callback when a resume future completes", () async { + var resumed = false; + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer( + handleResume: expectAsync1((inner) { + resumed = true; + inner.resume(); + }))) + .listen(expectAsync1((_) {}, count: 0)); + + var completer = new Completer(); + subscription.pause(completer.future); + await flushMicrotasks(); + expect(resumed, isFalse); + + completer.complete(); + await flushMicrotasks(); + expect(resumed, isTrue); + }); + + test("doesn't invoke the callback when the subscription has been canceled", + () async { + var controller = new StreamController(); + var subscription = controller.stream + .transform(subscriptionTransformer( + handlePause: expectAsync1((_) {}, count: 0))) + .listen(expectAsync1((_) {}, count: 0)); + + subscription.cancel(); + subscription.resume(); + subscription.resume(); + subscription.resume(); + }); + }); + + group("when the outer subscription is canceled but the inner is not", () { + StreamSubscription subscription; + setUp(() { + var controller = new StreamController(); + subscription = controller.stream + .transform(subscriptionTransformer(handleCancel: (_) {})) + .listen( + expectAsync1((_) {}, count: 0), + onError: expectAsync2((_, __) {}, count: 0), + onDone: expectAsync0(() {}, count: 0)); + subscription.cancel(); + controller.add(1); + controller.addError("oh no!"); + controller.close(); + }); + + test("doesn't call a new onData", () async { + subscription.onData(expectAsync1((_) {}, count: 0)); + await flushMicrotasks(); + }); + + test("doesn't call a new onError", () async { + subscription.onError(expectAsync2((_, __) {}, count: 0)); + await flushMicrotasks(); + }); + + test("doesn't call a new onDone", () async { + subscription.onDone(expectAsync0(() {}, count: 0)); + await flushMicrotasks(); + }); + + test("isPaused returns false", () { + expect(subscription.isPaused, isFalse); + }); + + test("asFuture never completes", () async { + subscription.asFuture().then(expectAsync1((_) {}, count: 0)); + await flushMicrotasks(); + }); + }); +} From 5474767bd346c8b19f47073dde11e48875a085b7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 24 Jan 2017 13:30:55 -0800 Subject: [PATCH 076/260] Update the README. (dart-lang/async#17) --- pkgs/async/README.md | 96 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 16cd55f5..60e7b0bf 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -1,15 +1,93 @@ Contains utility classes in the style of `dart:async` to work with asynchronous computations. -### Zipping streams +* The [`AsyncCache`][AsyncCache] class allows expensive asynchronous + computations values to be cached for a period of time. -The `StreamZip` class can combine several streams of events into a single stream -of tuples of events. +* The [`AsyncMemoizer`][AsyncMemoizer] class makes it easy to only run an + asynchronous operation once on demand. -### Results +* The [`CancelableOperation`][CancelableOperation] class defines an operation + that can be canceled by its consumer. The producer can then listen for this + cancellation and stop producing the future when it's received. It can be + created using a [`CancelableCompleter`][CancelableCompleter]. -The package introduces a `Result` class that can hold either a value or an -error. It allows capturing an asynchronous computation which can give either a -value or an error, into an asynchronous computation that always gives a `Result` -value, where errors can be treated as data. It also allows releasing the -`Result` back into an asynchronous computation. +* The delegating wrapper classes allow users to easily add functionality on top + of existing instances of core types from `dart:async`. These include + [`DelegatingFuture`][DelegatingFuture], + [`DelegatingStream`][DelegatingStream], + [`DelegatingStreamSubscription`][DelegatingStreamSubscription], + [`DelegatingStreamConsumer`][DelegatingStreamConsumer], + [`DelegatingSink`][DelegatingSink], + [`DelegatingEventSink`][DelegatingEventSink], and + [`DelegatingStreamSink`][DelegatingStreamSink]. + + The delegating classes all have `.typed()` constructors which allow users to + cast the generic type parameters in a way that's safe for strong mode. For + example, if `future` is a `Future` and you know it actually contains an + `int`, you can write `DelegatingFuture.typed(future)`. + +* The [`FutureGroup`][FutureGroup] class makes it easy to wait until a group of + features that may change over time completes. + +* The [`LazyStream`][LazyStream] class allows a stream to be initialized lazily + when `.listen()` is first called. + +* The [`NullStreamSink`][NullStreamSink] class is an implementation of + `StreamSink` that discards all events. + +* The [`RestartableTimer`][RestartableTimer] class extends `Timer` with a + `reset()` method. + +* The [`Result`][Result] class that can hold either a value or an error. It + provides various utilities for converting to and from `Future`s and `Stream`s. + +* The [`StreamGroup`][StreamGroup] class merges a collection of streams into a + single output stream. + +* The [`StreamQueue`][StreamQueue] class allows a stream to be consumed + event-by-event rather than being pushed whichever events as soon as they + arrive. + +* The [`StreamSplitter`][StreamSplitter] class allows a stream to be duplicated + into multiple identical streams. + +* The [`StreamZip`][StreamZip] class combines multiple streams into a single + stream of lists of events. + +* This package contains a number of [`StreamTransformer`][StreamTransformer]s. + [`SingleSubscriptionTransformer`][SingleSubscriptionTransformer] converts a + broadcast stream to a single-subscription stream, and + [`typedStreamTransformer`][typedStreamTransformer] casts the type of a + `Stream`. It also defines a transformer type for [`StreamSink`][StreamSink]s, + [`StreamSinkTransformer`][StreamSinkTransformer]. + +* The [`SubscriptionStream`][SubscriptionStream] class wraps a + `StreamSubscription` so it can be re-used as a `Stream`. + +[AsyncCache]: https://www.dartdocs.org/documentation/async/latest/async/AsyncCache-class.html +[AsyncMemoizer]: https://www.dartdocs.org/documentation/async/latest/async/AsyncMemoizer-class.html +[CancelableCompleter]: https://www.dartdocs.org/documentation/async/latest/async/CancelableCompleter-class.html +[CancelableOperation]: https://www.dartdocs.org/documentation/async/latest/async/CancelableOperation-class.html +[DelegatingEventSink]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingEventSink-class.html +[DelegatingFuture]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingFuture-class.html +[DelegatingSink]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingSink-class.html +[DelegatingStreamConsumer]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStreamConsumer-class.html +[DelegatingStreamSink]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStreamSink-class.html +[DelegatingStreamSubscription]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStreamSubscription-class.html +[DelegatingStream]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStream-class.html +[FutureGroup]: https://www.dartdocs.org/documentation/async/latest/async/FutureGroup-class.html +[LazyStream]: https://www.dartdocs.org/documentation/async/latest/async/LazyStream-class.html +[NullStreamSink]: https://www.dartdocs.org/documentation/async/latest/async/NullStreamSink-class.html +[RestartableTimer]: https://www.dartdocs.org/documentation/async/latest/async/RestartableTimer-class.html +[Result]: https://www.dartdocs.org/documentation/async/latest/async/Result-class.html +[SingleSubscriptionTransformer]: https://www.dartdocs.org/documentation/async/latest/async/SingleSubscriptionTransformer-class.html +[StreamGroup]: https://www.dartdocs.org/documentation/async/latest/async/StreamGroup-class.html +[StreamQueue]: https://www.dartdocs.org/documentation/async/latest/async/StreamQueue-class.html +[StreamSinkTransformer]: https://www.dartdocs.org/documentation/async/latest/async/StreamSinkTransformer-class.html +[StreamSink]: https://api.dartlang.org/stable/latest/dart-async/StreamSink-class.html +[StreamSplitter]: https://www.dartdocs.org/documentation/async/latest/async/StreamSplitter-class.html +[StreamTransformer]: https://api.dartlang.org/stable/latest/dart-async/StreamTransformer-class.html +[StreamZip]: https://www.dartdocs.org/documentation/async/latest/async/StreamZip-class.html +[SubscriptionStream]: https://www.dartdocs.org/documentation/async/latest/async/SubscriptionStream-class.html +[typedStreamTransformer]: https://www.dartdocs.org/documentation/async/latest/async/typedStreamTransformer.html From 032ef6fa0dd984bc99b791315cc5bb4aafac7d95 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 24 Jan 2017 13:32:22 -0800 Subject: [PATCH 077/260] Two small strong-mode/warning fixes (dart-lang/async#20) --- pkgs/async/lib/src/stream_subscription_transformer.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart index 060f4664..c13341cc 100644 --- a/pkgs/async/lib/src/stream_subscription_transformer.dart +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'async_memoizer.dart'; -import 'delegate/stream_subscription.dart'; typedef Future _AsyncHandler(StreamSubscription inner); @@ -101,5 +100,5 @@ class _TransformedSubscription implements StreamSubscription { } Future/**/ asFuture/**/([/*=E*/ futureValue]) => - _inner?.asFuture(futureValue) ?? new Completer().future; + _inner?.asFuture(futureValue) ?? new Completer/**/().future; } From 29158fd91fb595a0601a1a5f2e5b1ecb3bac85ec Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Tue, 24 Jan 2017 13:32:39 -0800 Subject: [PATCH 078/260] Fix a spelling mistake (dart-lang/async#21) --- pkgs/async/lib/src/stream_zip.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index f9e20825..a65840cb 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -7,7 +7,7 @@ import "dart:async"; /// A stream that combines the values of other streams. /// /// This emits lists of collected values from each input stream. The first list -/// contains the first value emitted by each stream, the second contrains the +/// contains the first value emitted by each stream, the second contains the /// second value, and so on. The lists have the same ordering as the iterable /// passed to [new StreamZip]. /// From 53c7aa54090d60d5aef0ec7d371a8ca60a63b8a4 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 24 Jan 2017 13:32:50 -0800 Subject: [PATCH 079/260] Add StreamQueue.eventsDispatched. (dart-lang/async#19) This is useful for advanced transaction operations, such as "one of the following consumers accepts the queue". We want to reliably accept the same consumer every time, and the best way to do that is to accept the one that consumed the most events. --- pkgs/async/CHANGELOG.md | 3 ++ pkgs/async/lib/src/stream_queue.dart | 37 ++++++++--------------- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_queue_test.dart | 41 ++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 43fcf54f..c49a4bd4 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -9,6 +9,9 @@ * Add `StreamQueue.cancelable()`, which allows users to easily make a `CancelableOperation` that can be canceled without affecting the queue. +* Add `StreamQueue.eventsDispatched` which counts the number of events that have + been dispatched by a given queue. + * Add a `subscriptionTransformer()` function to create `StreamTransformer`s that modify the behavior of subscriptions to a stream. diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index f770e16e..7b73eace 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -89,6 +89,15 @@ abstract class StreamQueue { /// Closing operations are [cancel] and [rest]. bool _isClosed = false; + /// The number of events dispatched by this queue. + /// + /// This counts error events. It doesn't count done events, or events + /// dispatched to a stream returned by [rest]. + int get eventsDispatched => _eventsReceived - _eventQueue.length; + + /// The number of events received by this queue. + var _eventsReceived = 0; + /// Queue of events not used by a request yet. final QueueList _eventQueue = new QueueList(); @@ -420,6 +429,7 @@ abstract class StreamQueue { /// Called when the event source adds a new data or error event. /// Always calls [_updateRequests] after adding. void _addResult(Result result) { + _eventsReceived++; _eventQueue.add(result); _updateRequests(); } @@ -540,7 +550,7 @@ class StreamQueueTransaction { final StreamSplitter _splitter; /// Queues created using [newQueue]. - final _queues = new Set<_TransactionStreamQueue>(); + final _queues = new Set(); /// Whether [commit] has been called. var _committed = false; @@ -557,7 +567,7 @@ class StreamQueueTransaction { /// [StreamQueue.startTransaction] was called. Its position can be committed /// to the parent queue using [commit]. StreamQueue newQueue() { - var queue = new _TransactionStreamQueue(_splitter.split()); + var queue = new StreamQueue(_splitter.split()); _queues.add(queue); return queue; } @@ -582,9 +592,7 @@ class StreamQueueTransaction { // Remove all events from the parent queue that were consumed by the // child queue. - var eventsConsumed = (queue as _TransactionStreamQueue)._eventsReceived - - queue._eventQueue.length; - for (var j = 0; j < eventsConsumed; j++) { + for (var j = 0; j < queue.eventsDispatched; j++) { _parent._eventQueue.removeFirst(); } @@ -628,25 +636,6 @@ class StreamQueueTransaction { } } -/// A [StreamQueue] that belongs to a [StreamQueueTransaction]. -class _TransactionStreamQueue extends _StreamQueue { - /// The total number of events received by this queue, including events that - /// haven't yet been consumed by requests. - /// - /// This is used to fast-forward the parent queue if this transaction is - /// accepted. - var _eventsReceived = 0; - - _TransactionStreamQueue(Stream sourceStream) : super(sourceStream); - - /// Modifies [StreamQueue._addResult] to count the total number of events that - /// have been passed to this transaction. - void _addResult(Result result) { - _eventsReceived++; - super._addResult(result); - } -} - /// Request object that receives events when they arrive, until fulfilled. /// /// Each request that cannot be fulfilled immediately is represented by diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 20ec7339..ec62ff2d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.12.0-dev +version: 1.12.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 02a77333..0aebb17a 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -42,6 +42,47 @@ main() { }); }); + group("eventsDispatched", () { + test("increments after a next future completes", () async { + var events = new StreamQueue(createStream()); + + expect(events.eventsDispatched, equals(0)); + await flushMicrotasks(); + expect(events.eventsDispatched, equals(0)); + + var next = events.next; + expect(events.eventsDispatched, equals(0)); + + await next; + expect(events.eventsDispatched, equals(1)); + + await events.next; + expect(events.eventsDispatched, equals(2)); + }); + + test("increments multiple times for multi-value requests", () async { + var events = new StreamQueue(createStream()); + await events.take(3); + expect(events.eventsDispatched, equals(3)); + }); + + test("increments multiple times for an accepted transaction", () async { + var events = new StreamQueue(createStream()); + await events.withTransaction((queue) async { + await queue.next; + await queue.next; + return true; + }); + expect(events.eventsDispatched, equals(2)); + }); + + test("doesn't increment for rest requests", () async { + var events = new StreamQueue(createStream()); + await events.rest.toList(); + expect(events.eventsDispatched, equals(0)); + }); + }); + group("next operation", () { test("simple sequence of requests", () async { var events = new StreamQueue(createStream()); From f4d43ef19a642ff4ee140074389cb7178cb98c39 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 27 Jan 2017 12:48:23 +0100 Subject: [PATCH 080/260] Add `byteCollector` stream transformer and `collectBytes` function. BUG= https://github.com/dart-lang/typed_data/issues/2 R=nweiz@google.com Review-Url: https://codereview.chromium.org//2649233006 . --- pkgs/async/CHANGELOG.md | 5 +++ pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/byte_collector.dart | 42 +++++++++++++++++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/byte_collection_test.dart | 39 +++++++++++++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 pkgs/async/lib/src/byte_collector.dart create mode 100644 pkgs/async/test/byte_collection_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index c49a4bd4..1ac341ec 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.13.0 + +* Add a `collectBytes` function which collects list-of-byte events into + a single byte list. + ## 1.12.0 * Add an `AsyncCache` class that caches asynchronous operations for a period of diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index c11f0b56..e0900326 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -4,6 +4,7 @@ export "src/async_cache.dart"; export "src/async_memoizer.dart"; +export "src/byte_collector.dart"; export "src/cancelable_operation.dart"; export "src/delegate/event_sink.dart"; export "src/delegate/future.dart"; diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart new file mode 100644 index 00000000..3b4f0750 --- /dev/null +++ b/pkgs/async/lib/src/byte_collector.dart @@ -0,0 +1,42 @@ +// 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. + +import "dart:async"; +import "dart:typed_data"; + +/// Collects an asynchronous sequence of byte lists into a single list of bytes. +/// +/// If the [source] stream emits an error event, +/// the collection fails and the returned future completes with the same error. +/// +/// If any of the input data are not valid bytes, they will be truncated to +/// an eight-bit unsigned value in the resulting list. +Future collectBytes(Stream> source) { + var byteLists = List>[]; + var length = 0; + var completer = new Completer.sync(); + source.listen( + (bytes) { + byteLists.add(bytes); + length += bytes.length; + }, + onError: completer.completeError, + onDone: () { + completer.complete(_collect(length, byteLists)); + }, + cancelOnError: true); + return completer.future; +} + +// Join a lists of bytes with a known total length into a single [Uint8List]. +Uint8List _collect(int length, List> byteLists) { + var result = new Uint8List(length); + int i = 0; + for (var byteList in byteLists) { + var end = i + byteList.length; + result.setRange(i, end, byteList); + i = end; + } + return result; +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index ec62ff2d..a3e0c904 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.12.0 +version: 1.13.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart new file mode 100644 index 00000000..80685427 --- /dev/null +++ b/pkgs/async/test/byte_collection_test.dart @@ -0,0 +1,39 @@ +// 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. + +import "dart:async"; +import "dart:typed_data"; + +import "package:test/test.dart"; +import "package:async/async.dart" show byteCollector, collectBytes, Result; + +void main() { + group("collectBytes", () { + test("simple list and overflow", () { + var result = collectBytes(new Stream.fromIterable([ + [0], + [1], + [2], + [256] + ])); + expect(result, completion([0, 1, 2, 0])); + }); + + test("no events", () { + var result = collectBytes(new Stream.fromIterable([])); + expect(result, completion([])); + }); + + test("empty events", () { + var result = collectBytes(new Stream.fromIterable([[], []])); + expect(result, completion([])); + }); + + test("error event", () { + var result = collectBytes(new Stream.fromIterable( + new Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); + expect(result, throwsA("badness")); + }); + }); +} From a3f9649ba7111cac119e4f29d4f763ed72f67562 Mon Sep 17 00:00:00 2001 From: Lasse Reichstein Holst Nielsen Date: Tue, 31 Jan 2017 12:40:07 +0100 Subject: [PATCH 081/260] Add `peek` and `lookAhead` to `StreamQueue`. These allow users to look at events (similar to `next` and `take`) without consuming them. For simple cases, they can be used instead of `startTransaction` to decide what to do before doing it. R=nweiz@google.com Review-Url: https://codereview.chromium.org//2649033006 . --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/src/stream_queue.dart | 104 ++++++++++++++++++++++--- pkgs/async/test/stream_queue_test.dart | 94 ++++++++++++++++++++++ 3 files changed, 192 insertions(+), 9 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1ac341ec..5809285c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -8,6 +8,9 @@ * Add an `AsyncCache` class that caches asynchronous operations for a period of time. +* Add `StreamQueue.peek` and `StreamQueue.lookAheead`. + These allow users to look at events without consuming them. + * Add `StreamQueue.startTransaction()` and `StreamQueue.withTransaction()`. These allow users to conditionally consume events based on their values. diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 7b73eace..14eb7a0a 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -131,6 +131,22 @@ abstract class StreamQueue { throw _failClosed(); } + + /// Look at the next [count] data events without consuming them. + /// + /// Works like [take] except that the events are left in the queue. + /// If one of the next [count] events is an error, the returned future + /// completes with this error, and the error is still left in the queue. + Future> lookAhead(int count) { + if (count < 0) throw new RangeError.range(count, 0, null, "count"); + if (!_isClosed) { + var request = new _LookAheadRequest(count); + _addRequest(request); + return request.future; + } + throw _failClosed(); + } + /// Requests the next (yet unrequested) event from the stream. /// /// When the requested event arrives, the returned future is completed with @@ -154,6 +170,19 @@ abstract class StreamQueue { throw _failClosed(); } + /// Looks at the next (yet unrequested) event from the stream. + /// + /// Like [next] except that the event is not consumed. + /// If the next event is an error event, it stays in the queue. + Future get peek { + if (!_isClosed) { + var nextRequest = new _PeekRequest(); + _addRequest(nextRequest); + return nextRequest.future; + } + throw _failClosed(); + } + /// Returns a stream of all the remaning events of the source stream. /// /// All requested [next], [skip] or [take] operations are completed @@ -353,8 +382,8 @@ abstract class StreamQueue { /// `cancel`. /// /// After calling `cancel`, no further events can be requested. - /// None of [next], [rest], [skip], [take] or [cancel] may be - /// called again. + /// None of [lookAhead], [next], [peek], [rest], [skip], [take] or [cancel] + /// may be called again. Future cancel({bool immediate: false}) { if (_isClosed) throw _failClosed(); _isClosed = true; @@ -692,15 +721,42 @@ class _NextRequest implements _EventRequest { return true; } if (isDone) { - var errorFuture = - new Future.sync(() => throw new StateError("No elements")); - _completer.complete(errorFuture); + _completer.completeError(new StateError("No elements"), + StackTrace.current); return true; } return false; } } + +/// Request for a [StreamQueue.peek] call. +/// +/// Completes the returned future when receiving the first event, +/// and is then complete, but doesn't consume the event. +class _PeekRequest implements _EventRequest { + /// Completer for the future returned by [StreamQueue.next]. + final _completer = new Completer(); + + _PeekRequest(); + + Future get future => _completer.future; + + bool update(QueueList> events, bool isDone) { + if (events.isNotEmpty) { + events.first.complete(_completer); + return true; + } + if (isDone) { + _completer.completeError(new StateError("No elements"), + StackTrace.current); + return true; + } + return false; + } +} + + /// Request for a [StreamQueue.skip] call. class _SkipRequest implements _EventRequest { /// Completer for the future returned by the skip call. @@ -738,8 +794,8 @@ class _SkipRequest implements _EventRequest { } } -/// Request for a [StreamQueue.take] call. -class _TakeRequest implements _EventRequest { +/// Common superclass for [_TakeRequest] and [_LookAheadRequest]. +abstract class _ListRequest implements _EventRequest { /// Completer for the future returned by the take call. final _completer = new Completer>(); @@ -752,10 +808,16 @@ class _TakeRequest implements _EventRequest { /// this value. final int _eventsToTake; - _TakeRequest(this._eventsToTake); + _ListRequest(this._eventsToTake); /// The future completed when the correct number of events have been captured. Future> get future => _completer.future; +} + + +/// Request for a [StreamQueue.take] call. +class _TakeRequest extends _ListRequest { + _TakeRequest(int eventsToTake) : super(eventsToTake); bool update(QueueList> events, bool isDone) { while (_list.length < _eventsToTake) { @@ -766,7 +828,7 @@ class _TakeRequest implements _EventRequest { var event = events.removeFirst(); if (event.isError) { - _completer.completeError(event.asError.error, event.asError.stackTrace); + event.asError.complete(_completer); return true; } _list.add(event.asValue.value); @@ -776,6 +838,30 @@ class _TakeRequest implements _EventRequest { } } + +/// Request for a [StreamQueue.lookAhead] call. +class _LookAheadRequest extends _ListRequest { + _LookAheadRequest(int eventsToTake) : super(eventsToTake); + + bool update(QueueList> events, bool isDone) { + while (_list.length < _eventsToTake) { + if (events.length == _list.length) { + if (isDone) break; + return false; + } + var event = events.elementAt(_list.length); + if (event.isError) { + event.asError.complete(_completer); + return true; + } + _list.add(event.asValue.value); + } + _completer.complete(_list); + return true; + } +} + + /// Request for a [StreamQueue.cancel] call. /// /// The request needs no events, it just waits in the request queue diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 0aebb17a..9668f0a0 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -83,6 +83,65 @@ main() { }); }); + group("lookAhead operation", () { + test("as simple list of events", () async { + var events = new StreamQueue(createStream()); + expect(await events.lookAhead(4), [1, 2, 3, 4]); + expect(await events.next, 1); + expect(await events.lookAhead(2), [2, 3]); + expect(await events.take(2), [2, 3]); + expect(await events.next, 4); + await events.cancel(); + }); + + test("of 0 events", () async { + var events = new StreamQueue(createStream()); + expect(events.lookAhead(0), completion([])); + expect(events.next, completion(1)); + expect(events.lookAhead(0), completion([])); + expect(events.next, completion(2)); + expect(events.lookAhead(0), completion([])); + expect(events.next, completion(3)); + expect(events.lookAhead(0), completion([])); + expect(events.next, completion(4)); + expect(events.lookAhead(0), completion([])); + expect(events.lookAhead(5), completion([])); + expect(events.next, throwsStateError); + await events.cancel(); + }); + + test("with bad arguments throws", () async { + var events = new StreamQueue(createStream()); + expect(() => events.lookAhead(-1), throwsArgumentError); + expect(await events.next, 1); // Did not consume event. + expect(() => events.lookAhead(-1), throwsArgumentError); + expect(await events.next, 2); // Did not consume event. + await events.cancel(); + }); + + test("of too many arguments", () async { + var events = new StreamQueue(createStream()); + expect(await events.lookAhead(6), [1, 2, 3, 4]); + await events.cancel(); + }); + + test("too large later", () async { + var events = new StreamQueue(createStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(await events.lookAhead(6), [3, 4]); + await events.cancel(); + }); + + test("error", () async { + var events = new StreamQueue(createErrorStream()); + expect(events.lookAhead(4), throwsA("To err is divine!")); + expect(events.take(4), throwsA("To err is divine!")); + expect(await events.next, 4); + await events.cancel(); + }); + }); + group("next operation", () { test("simple sequence of requests", () async { var events = new StreamQueue(createStream()); @@ -374,11 +433,46 @@ main() { }); }); + group("peek operation", () { + test("peeks one event", () async { + var events = new StreamQueue(createStream()); + expect(await events.peek, 1); + expect(await events.next, 1); + expect(await events.peek, 2); + expect(await events.take(2), [2, 3]); + expect(await events.peek, 4); + expect(await events.next, 4); + // Throws at end. + expect(events.peek, throws); + await events.cancel(); + }); + test("multiple requests at the same time", () async { + var events = new StreamQueue(createStream()); + var result = await Future.wait( + [events.peek, events.peek, events.next, events.peek, events.peek]); + expect(result, [1, 1, 1, 2, 2]); + await events.cancel(); + }); + test("sequence of requests with error", () async { + var events = new StreamQueue(createErrorStream()); + expect(await events.next, 1); + expect(await events.next, 2); + expect(events.peek, throwsA("To err is divine!")); + // Error stays in queue. + expect(events.peek, throwsA("To err is divine!")); + expect(events.next, throwsA("To err is divine!")); + expect(await events.next, 4); + await events.cancel(); + }); + }); + group("cancel operation", () { test("closes the events, prevents any other operation", () async { var events = new StreamQueue(createStream()); await events.cancel(); + expect(() => events.lookAhead(1), throwsStateError); expect(() => events.next, throwsStateError); + expect(() => events.peek, throwsStateError); expect(() => events.skip(1), throwsStateError); expect(() => events.take(1), throwsStateError); expect(() => events.rest, throwsStateError); From eeb15aaa831e09e93dbb38e067113dc2385aac21 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 2 Feb 2017 13:39:46 -0800 Subject: [PATCH 082/260] Bug a bug in StreamQueueTransaction.reject(). (dart-lang/async#22) This would throw a StateError if StreamQueue.rest had been called on one of the transaction's child queues, because that queue didn't expect to be canceled later on. --- pkgs/async/CHANGELOG.md | 3 +++ pkgs/async/lib/src/stream_queue.dart | 4 ++-- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_queue_test.dart | 24 ++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 5809285c..1984b284 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,9 @@ * Add a `collectBytes` function which collects list-of-byte events into a single byte list. +* Fix a bug where rejecting a `StreamQueueTransaction` would throw a + `StateError` if `StreamQueue.rest` had been called on one of its child queues. + ## 1.12.0 * Add an `AsyncCache` class that caches asynchronous operations for a period of diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 14eb7a0a..575cc8a1 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -548,6 +548,7 @@ class _StreamQueue extends StreamQueue { if (_isDone) { return new Stream.empty(); } + _isDone = true; if (_subscription == null) { return _sourceStream; @@ -555,7 +556,6 @@ class _StreamQueue extends StreamQueue { var subscription = _subscription; _subscription = null; - _isDone = true; var wasPaused = subscription.isPaused; var result = new SubscriptionStream(subscription); @@ -960,7 +960,7 @@ class _HasNextRequest implements _EventRequest { /// Request for a [StreamQueue.startTransaction] call. /// /// This request isn't complete until the user calls -/// [StreamQueueTransaction.commit] or [StreamQueue.rejectTransaction], at which +/// [StreamQueueTransaction.commit] or [StreamQueueTransaction.reject], at which /// point it manually removes itself from the request queue and calls /// [StreamQueue._updateRequests]. class _TransactionRequest implements _EventRequest { diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index a3e0c904..0e703f70 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.0 +version: 1.13.0-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 9668f0a0..7d2fdd49 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -863,6 +863,30 @@ main() { transaction.reject(); }); + // Regression test. + test("pending child rest requests emit no more events", () async { + var controller = new StreamController(); + var events = new StreamQueue(controller.stream); + var transaction = events.startTransaction(); + var queue = transaction.newQueue(); + + // This should emit no more events after the transaction is rejected. + queue.rest.listen(expectAsync1((_) {}, count: 3), + onDone: expectAsync0(() {}, count: 0)); + + controller.add(1); + controller.add(2); + controller.add(3); + await flushMicrotasks(); + + transaction.reject(); + await flushMicrotasks(); + + // These shouldn't affect the result of `queue.rest.toList()`. + controller.add(4); + controller.add(5); + }); + test("child requests' cancel() may still be called explicitly", () async { transaction.reject(); await queue1.cancel(); From 276e2178f6286b8909597e7a8541872065b828c6 Mon Sep 17 00:00:00 2001 From: Lasse Reichstein Holst Nielsen Date: Fri, 3 Feb 2017 16:23:28 +0100 Subject: [PATCH 083/260] Add collectBytesCancelable function. R=nweiz@google.com Review-Url: https://codereview.chromium.org//2661603002 . --- pkgs/async/CHANGELOG.md | 4 +- pkgs/async/lib/src/byte_collector.dart | 36 +++++++++++++-- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/byte_collection_test.dart | 54 ++++++++++++++++++++++- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1984b284..adf53c75 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,7 +1,7 @@ ## 1.13.0 -* Add a `collectBytes` function which collects list-of-byte events into - a single byte list. +* Add `collectBytes` and `collectBytesCancelable` functions which collects + list-of-byte events into a single byte list. * Fix a bug where rejecting a `StreamQueueTransaction` would throw a `StateError` if `StreamQueue.rest` had been called on one of its child queues. diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart index 3b4f0750..a8d2dbf4 100644 --- a/pkgs/async/lib/src/byte_collector.dart +++ b/pkgs/async/lib/src/byte_collector.dart @@ -4,6 +4,7 @@ import "dart:async"; import "dart:typed_data"; +import "cancelable_operation.dart"; /// Collects an asynchronous sequence of byte lists into a single list of bytes. /// @@ -13,10 +14,39 @@ import "dart:typed_data"; /// If any of the input data are not valid bytes, they will be truncated to /// an eight-bit unsigned value in the resulting list. Future collectBytes(Stream> source) { - var byteLists = List>[]; + return _collectBytes(source, (_, result) => result); +} + +/// Collects an asynchronous sequence of byte lists into a single list of bytes. +/// +/// Returns a [CancelableOperation] that provides the result future and a way +/// to cancel the collection early. +/// +/// If the [source] stream emits an error event, +/// the collection fails and the returned future completes with the same error. +/// +/// If any of the input data are not valid bytes, they will be truncated to +/// an eight-bit unsigned value in the resulting list. +CancelableOperation collectBytesCancelable( + Stream> source) { + return _collectBytes(source, (subscription, result) => + new CancelableOperation.fromFuture(result, onCancel: subscription.cancel) + ); +} + +/// Generalization over [collectBytes] and [collectBytesCancelable]. +/// +/// Performs all the same operations, but the final result is created +/// by the [result] function, which has access to the stream subscription +/// so it can cancel the operation. +T _collectBytes( + Stream> source, + T result(StreamSubscription> subscription, + Future result)) { + var byteLists = >[]; var length = 0; var completer = new Completer.sync(); - source.listen( + var subscription = source.listen( (bytes) { byteLists.add(bytes); length += bytes.length; @@ -26,7 +56,7 @@ Future collectBytes(Stream> source) { completer.complete(_collect(length, byteLists)); }, cancelOnError: true); - return completer.future; + return result(subscription, completer.future); } // Join a lists of bytes with a known total length into a single [Uint8List]. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0e703f70..b2bdf6dc 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -10,4 +10,4 @@ dev_dependencies: stack_trace: "^1.0.0" test: "^0.12.0" environment: - sdk: ">=1.19.0 <2.0.0" + sdk: ">=1.21.0 <2.0.0" diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart index 80685427..74ab0435 100644 --- a/pkgs/async/test/byte_collection_test.dart +++ b/pkgs/async/test/byte_collection_test.dart @@ -6,7 +6,7 @@ import "dart:async"; import "dart:typed_data"; import "package:test/test.dart"; -import "package:async/async.dart" show byteCollector, collectBytes, Result; +import "package:async/async.dart"; void main() { group("collectBytes", () { @@ -36,4 +36,56 @@ void main() { expect(result, throwsA("badness")); }); }); + + group("collectBytes", () { + test("simple list and overflow", () { + var result = collectBytesCancelable(new Stream.fromIterable([ + [0], + [1], + [2], + [256] + ])); + expect(result.value, completion([0, 1, 2, 0])); + }); + + test("no events", () { + var result = collectBytesCancelable(new Stream.fromIterable([])); + expect(result.value, completion([])); + }); + + test("empty events", () { + var result = collectBytesCancelable(new Stream.fromIterable([[], []])); + expect(result.value, completion([])); + }); + + test("error event", () { + var result = collectBytesCancelable(new Stream.fromIterable( + new Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); + expect(result.value, throwsA("badness")); + }); + + test("cancelled", () async { + var sc = new StreamController>(); + var result = collectBytesCancelable(sc.stream); + // Value never completes. + result.value.whenComplete(expectAsync0((){}, count: 0)); + + expect(sc.hasListener, isTrue); + sc.add([1, 2]); + await nextTimerTick(); + expect(sc.hasListener, isTrue); + sc.add([3, 4]); + await nextTimerTick(); + expect(sc.hasListener, isTrue); + result.cancel(); + expect(sc.hasListener, isFalse); // Cancelled immediately. + var replacement = await result.valueOrCancellation(); + expect(replacement, isNull); + await nextTimerTick(); + sc.close(); + await nextTimerTick(); + }); + }); } + +Future nextTimerTick() => new Future((){}); From f0d5ffabb4f4361f6a619323f7c58a8a732e0cd2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 6 Feb 2017 15:40:29 -0800 Subject: [PATCH 084/260] Fix StreamQueue.withTransaction(). (dart-lang/async#24) --- pkgs/async/CHANGELOG.md | 3 +++ pkgs/async/lib/src/stream_queue.dart | 1 + pkgs/async/test/stream_queue_test.dart | 5 +++++ 3 files changed, 9 insertions(+) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index adf53c75..56855030 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -6,6 +6,9 @@ * Fix a bug where rejecting a `StreamQueueTransaction` would throw a `StateError` if `StreamQueue.rest` had been called on one of its child queues. +* `StreamQueue.withTransaction()` now properly returns whether or not the + transaction was committed. + ## 1.12.0 * Add an `AsyncCache` class that caches asynchronous operations for a period of diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 575cc8a1..99e74367 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -328,6 +328,7 @@ abstract class StreamQueue { } else { transaction.reject(); } + return result; }, onError: (error) { transaction.commit(queue); throw error; diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 7d2fdd49..f487e654 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -1004,6 +1004,11 @@ main() { expect(events.next, completion(3)); }); + + test("returns whether the transaction succeeded", () { + expect(events.withTransaction((_) async => true), completion(isTrue)); + expect(events.withTransaction((_) async => false), completion(isFalse)); + }); }); group("cancelable operation", () { From 58c5e5742174d35bba9864733fe73f7fa638a5a8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 6 Feb 2017 15:40:36 -0800 Subject: [PATCH 085/260] Release 1.13.0. (dart-lang/async#23) --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b2bdf6dc..6fce524c 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.0-dev +version: 1.13.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 3f264dab50617d381240fdf650bb96bbef3bafa2 Mon Sep 17 00:00:00 2001 From: Lasse Reichstein Holst Nielsen Date: Wed, 15 Feb 2017 07:44:46 +0100 Subject: [PATCH 086/260] Change generic comment syntax to real generic syntax. R=nweiz@google.com Review-Url: https://codereview.chromium.org//2660333005 . --- pkgs/async/CHANGELOG.md | 6 +++++ pkgs/async/lib/src/delegate/event_sink.dart | 4 +-- pkgs/async/lib/src/delegate/future.dart | 6 ++--- pkgs/async/lib/src/delegate/sink.dart | 4 +-- pkgs/async/lib/src/delegate/stream.dart | 4 +-- .../lib/src/delegate/stream_consumer.dart | 4 +-- pkgs/async/lib/src/delegate/stream_sink.dart | 4 +-- .../lib/src/delegate/stream_subscription.dart | 8 +++--- pkgs/async/lib/src/future_group.dart | 8 +++--- pkgs/async/lib/src/lazy_stream.dart | 4 +-- pkgs/async/lib/src/result.dart | 20 +++++++------- pkgs/async/lib/src/stream_completer.dart | 4 +-- pkgs/async/lib/src/stream_group.dart | 4 +-- pkgs/async/lib/src/stream_queue.dart | 6 ++--- pkgs/async/lib/src/stream_sink_completer.dart | 6 ++--- .../lib/src/stream_sink_transformer.dart | 4 +-- pkgs/async/lib/src/stream_splitter.dart | 4 +-- .../src/stream_subscription_transformer.dart | 12 ++++----- pkgs/async/lib/src/typed/future.dart | 2 +- pkgs/async/lib/src/typed/stream.dart | 26 +++++++++---------- .../lib/src/typed/stream_subscription.dart | 2 +- .../lib/src/typed_stream_transformer.dart | 4 +-- pkgs/async/pubspec.yaml | 2 +- 23 files changed, 77 insertions(+), 71 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 56855030..be8377c1 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,6 @@ ## 1.13.0 +<<<<<<< ours * Add `collectBytes` and `collectBytesCancelable` functions which collects list-of-byte events into a single byte list. @@ -8,6 +9,11 @@ * `StreamQueue.withTransaction()` now properly returns whether or not the transaction was committed. +======= +* Add a `collectBytes` function which collects list-of-byte events into + a single byte list. +* Switched to using generic method syntax. +>>>>>>> theirs ## 1.12.0 diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 14501b28..54c3e52b 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -22,8 +22,8 @@ class DelegatingEventSink implements EventSink { /// instance of `EventSink`, not `EventSink`. This means that calls to /// [add] may throw a [CastError] if the argument type doesn't match the /// reified type of [sink]. - static EventSink/**/ typed/**/(EventSink sink) => - sink is EventSink/**/ ? sink : new DelegatingEventSink._(sink); + static EventSink typed(EventSink sink) => + sink is EventSink ? sink : new DelegatingEventSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index e30d41d9..4704343d 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -19,15 +19,15 @@ class DelegatingFuture implements Future { /// This soundly converts a [Future] to a `Future`, regardless of its /// original generic type, by asserting that its value is an instance of `T` /// whenever it's provided. If it's not, the future throws a [CastError]. - static Future/**/ typed/**/(Future future) => - future is Future/**/ ? future : new TypeSafeFuture/**/(future); + static Future typed(Future future) => + future is Future ? future : new TypeSafeFuture(future); Stream asStream() => _future.asStream(); Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); - Future/**/ then/**/(dynamic onValue(T value), {Function onError}) => + Future then(dynamic onValue(T value), {Function onError}) => _future.then(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index 326c15bd..7c68a0fb 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -20,8 +20,8 @@ class DelegatingSink implements Sink { /// instance of `Sink`, not `Sink`. This means that calls to [add] may /// throw a [CastError] if the argument type doesn't match the reified type of /// [sink]. - static Sink/**/ typed/**/(Sink sink) => - sink is Sink/**/ ? sink : new DelegatingSink._(sink); + static Sink typed(Sink sink) => + sink is Sink ? sink : new DelegatingSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/stream.dart b/pkgs/async/lib/src/delegate/stream.dart index 75622187..d08b4ab8 100644 --- a/pkgs/async/lib/src/delegate/stream.dart +++ b/pkgs/async/lib/src/delegate/stream.dart @@ -23,6 +23,6 @@ class DelegatingStream extends StreamView { /// original generic type, by asserting that its events are instances of `T` /// whenever they're provided. If they're not, the stream throws a /// [CastError]. - static Stream/**/ typed/**/(Stream stream) => - stream is Stream/**/ ? stream : new TypeSafeStream/**/(stream); + static Stream typed(Stream stream) => + stream is Stream ? stream : new TypeSafeStream(stream); } diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index 4f495d0e..ba830408 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -22,8 +22,8 @@ class DelegatingStreamConsumer implements StreamConsumer { /// instance of `StreamConsumer`, not `StreamConsumer`. This means that /// calls to [addStream] may throw a [CastError] if the argument type doesn't /// match the reified type of [consumer]. - static StreamConsumer/**/ typed/**/(StreamConsumer consumer) => - consumer is StreamConsumer/**/ + static StreamConsumer typed(StreamConsumer consumer) => + consumer is StreamConsumer ? consumer : new DelegatingStreamConsumer._(consumer); diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index 9b52b19e..198df8a7 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -24,8 +24,8 @@ class DelegatingStreamSink implements StreamSink { /// of `StreamSink`, not `StreamSink`. This means that calls to [add] may /// throw a [CastError] if the argument type doesn't match the reified type of /// [sink]. - static StreamSink/**/ typed/**/(StreamSink sink) => - sink is StreamSink/**/ ? sink : new DelegatingStreamSink._(sink); + static StreamSink typed(StreamSink sink) => + sink is StreamSink ? sink : new DelegatingStreamSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 5fa7b017..08234122 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -23,11 +23,11 @@ class DelegatingStreamSubscription implements StreamSubscription { /// regardless of its original generic type, by asserting that its events are /// instances of `T` whenever they're provided. If they're not, the /// subscription throws a [CastError]. - static StreamSubscription/**/ typed/**/( + static StreamSubscription typed( StreamSubscription subscription) => - subscription is StreamSubscription/**/ + subscription is StreamSubscription ? subscription - : new TypeSafeStreamSubscription/**/(subscription); + : new TypeSafeStreamSubscription(subscription); void onData(void handleData(T data)) { _source.onData(handleData); @@ -51,7 +51,7 @@ class DelegatingStreamSubscription implements StreamSubscription { Future cancel() => _source.cancel(); - Future/**/ asFuture/**/([/*=E*/ futureValue]) => + Future asFuture([E futureValue]) => _source.asFuture(futureValue); bool get isPaused => _source.isPaused; diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 114c1334..9b85c2fc 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -67,19 +67,19 @@ class FutureGroup implements Sink> { _pending++; task.then((value) { - if (_completer.isCompleted) return; + if (_completer.isCompleted) return null; _pending--; _values[index] = value; - if (_pending != 0) return; + if (_pending != 0) return null; if (_onIdleController != null) _onIdleController.add(null); - if (!_closed) return; + if (!_closed) return null; if (_onIdleController != null) _onIdleController.close(); _completer.complete(_values); }).catchError((error, stackTrace) { - if (_completer.isCompleted) return; + if (_completer.isCompleted) return null; _completer.completeError(error, stackTrace); }); } diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index 147fa8bd..ad00a039 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -43,10 +43,10 @@ class LazyStream extends Stream { Stream stream; if (result is Future) { stream = StreamCompleter.fromFuture(result.then((stream) { - return DelegatingStream.typed/**/(stream as Stream); + return DelegatingStream.typed(stream as Stream); })); } else { - stream = DelegatingStream.typed/**/(result as Stream); + stream = DelegatingStream.typed(result as Stream); } return stream.listen(onData, diff --git a/pkgs/async/lib/src/result.dart b/pkgs/async/lib/src/result.dart index 393dcedd..51db558c 100644 --- a/pkgs/async/lib/src/result.dart +++ b/pkgs/async/lib/src/result.dart @@ -76,10 +76,10 @@ abstract class Result { /// /// The resulting future will never have an error. /// Errors have been converted to an [ErrorResult] value. - static Future*/> capture/**/(Future/**/ future) { + static Future> capture(Future future) { return future.then((value) => new ValueResult(value), onError: (error, stackTrace) => - new ErrorResult/**/(error, stackTrace)); + new ErrorResult(error, stackTrace)); } /// Release the result of a captured future. @@ -89,23 +89,23 @@ abstract class Result { /// /// If [future] completes with an error, the returned future completes with /// the same error. - static Future/**/ release/**/(Future*/> future) => - future.then/*>*/((result) => result.asFuture); + static Future release(Future> future) => + future.then>((result) => result.asFuture); /// Capture the results of a stream into a stream of [Result] values. /// /// The returned stream will not have any error events. /// Errors from the source stream have been converted to [ErrorResult]s. - static Stream*/> captureStream/**/(Stream/**/ source) => - source.transform(new CaptureStreamTransformer/**/()); + static Stream> captureStream(Stream source) => + source.transform(new CaptureStreamTransformer()); /// Release a stream of [result] values into a stream of the results. /// /// `Result` values of the source stream become value or error events in /// the returned stream as appropriate. /// Errors from the source stream become errors in the returned stream. - static Stream/**/ releaseStream/**/(Stream*/> source) => - source.transform(new ReleaseStreamTransformer/**/()); + static Stream releaseStream(Stream> source) => + source.transform(new ReleaseStreamTransformer()); /// Converts a result of a result to a single result. /// @@ -113,9 +113,9 @@ abstract class Result { /// which is then an error, then a result with that error is returned. /// Otherwise both levels of results are value results, and a single /// result with the value is returned. - static Result/**/ flatten/**/(Result*/> result) { + static Result flatten(Result> result) { if (result.isValue) return result.asValue.value; - return new ErrorResult/**/( + return new ErrorResult( result.asError.error, result.asError.stackTrace); } diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index 953985e3..2bff6a5b 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -34,8 +34,8 @@ class StreamCompleter { /// /// If the future completes with an error, the returned stream will /// instead contain just that error. - static Stream/**/ fromFuture/**/(Future*/> streamFuture) { - var completer = new StreamCompleter/**/(); + static Stream fromFuture(Future> streamFuture) { + var completer = new StreamCompleter(); streamFuture.then(completer.setSourceStream, onError: completer.setError); return completer.stream; diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 889ccd82..4aa448e3 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -53,8 +53,8 @@ class StreamGroup implements Sink> { /// /// This is equivalent to adding [streams] to a group, closing that group, and /// returning its stream. - static Stream/**/ merge/**/(Iterable*/> streams) { - var group = new StreamGroup/**/(); + static Stream merge(Iterable> streams) { + var group = new StreamGroup(); streams.forEach(group.add); group.close(); return group.stream; diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 99e74367..f5780817 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -354,10 +354,10 @@ abstract class StreamQueue { /// CancelableOperation nextStdinLine() => /// _stdinQueue.cancelable((queue) => queue.next); /// ``` - CancelableOperation/**/ cancelable/**/( - Future/**/ callback(StreamQueue queue)) { + CancelableOperation cancelable( + Future callback(StreamQueue queue)) { var transaction = startTransaction(); - var completer = new CancelableCompleter/**/(onCancel: () { + var completer = new CancelableCompleter(onCancel: () { transaction.reject(); }); diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index 00a7086c..f8056c78 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -36,9 +36,9 @@ class StreamSinkCompleter { /// /// If the future completes with an error, the returned sink will instead /// be closed. Its [Sink.done] future will contain the error. - static StreamSink/**/ fromFuture/**/( - Future*/> sinkFuture) { - var completer = new StreamSinkCompleter/**/(); + static StreamSink fromFuture( + Future> sinkFuture) { + var completer = new StreamSinkCompleter(); sinkFuture.then(completer.setDestinationSink, onError: completer.setError); return completer.sink; diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index d40bc4b7..65adda67 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -54,9 +54,9 @@ abstract class StreamSinkTransformer { /// This means that calls to [StreamSink.add] on the returned sink may throw a /// [CastError] if the argument type doesn't match the reified type of the /// sink. - static StreamSinkTransformer/**/ typed/**/( + static StreamSinkTransformer typed( StreamSinkTransformer transformer) => - transformer is StreamSinkTransformer/**/ + transformer is StreamSinkTransformer ? transformer : new TypeSafeStreamSinkTransformer(transformer); } diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index 448fe2a2..3d8f994e 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -57,10 +57,10 @@ class StreamSplitter { /// /// [count] defaults to 2. This is the same as creating [count] branches and /// then closing the [StreamSplitter]. - static List*/> splitFrom/**/(Stream/**/ stream, + static List> splitFrom(Stream stream, [int count]) { if (count == null) count = 2; - var splitter = new StreamSplitter/**/(stream); + var splitter = new StreamSplitter(stream); var streams = new List.generate(count, (_) => splitter.split()); splitter.close(); return streams; diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart index c13341cc..ad545393 100644 --- a/pkgs/async/lib/src/stream_subscription_transformer.dart +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -27,10 +27,10 @@ typedef void _VoidHandler(StreamSubscription inner); /// synchronously call the corresponding method** on the inner /// [StreamSubscription]: [handleCancel] must call `cancel()`, [handlePause] /// must call `pause()`, and [handleResume] must call `resume()`. -StreamTransformer/**/ subscriptionTransformer/**/( - {Future handleCancel(StreamSubscription/**/ inner), - void handlePause(StreamSubscription/**/ inner), - void handleResume(StreamSubscription/**/ inner)}) { +StreamTransformer subscriptionTransformer( + {Future handleCancel(StreamSubscription inner), + void handlePause(StreamSubscription inner), + void handleResume(StreamSubscription inner)}) { return new StreamTransformer((stream, cancelOnError) { return new _TransformedSubscription( stream.listen(null, cancelOnError: cancelOnError), @@ -99,6 +99,6 @@ class _TransformedSubscription implements StreamSubscription { _handleResume(_inner); } - Future/**/ asFuture/**/([/*=E*/ futureValue]) => - _inner?.asFuture(futureValue) ?? new Completer/**/().future; + Future asFuture([E futureValue]) => + _inner?.asFuture(futureValue) ?? new Completer().future; } diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart index 995f4b5c..4630af7d 100644 --- a/pkgs/async/lib/src/typed/future.dart +++ b/pkgs/async/lib/src/typed/future.dart @@ -14,7 +14,7 @@ class TypeSafeFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) async => new TypeSafeFuture(_future.catchError(onError, test: test)); - Future/**/ then/**/(dynamic onValue(T value), {Function onError}) => + Future then(dynamic onValue(T value), {Function onError}) => _future.then((value) => onValue(value as T), onError: onError); Future whenComplete(action()) => diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart index afa4462d..235b205c 100644 --- a/pkgs/async/lib/src/typed/stream.dart +++ b/pkgs/async/lib/src/typed/stream.dart @@ -37,10 +37,10 @@ class TypeSafeStream implements Stream { onCancel(new TypeSafeStreamSubscription(subscription)))); } - Stream/**/ asyncExpand/**/(Stream/**/ convert(T event)) => + Stream asyncExpand(Stream convert(T event)) => _stream.asyncExpand(_validateType(convert)); - Stream/**/ asyncMap/**/(convert(T event)) => + Stream asyncMap(convert(T event)) => _stream.asyncMap(_validateType(convert)); Stream distinct([bool equals(T previous, T next)]) => @@ -48,10 +48,10 @@ class TypeSafeStream implements Stream { ? null : (previous, next) => equals(previous as T, next as T))); - Future/**/ drain/**/([/*=E*/ futureValue]) => + Future drain([E futureValue]) => _stream.drain(futureValue); - Stream/**/ expand/**/(Iterable/**/ convert(T value)) => + Stream expand(Iterable convert(T value)) => _stream.expand(_validateType(convert)); Future firstWhere(bool test(T element), {Object defaultValue()}) => @@ -63,8 +63,8 @@ class TypeSafeStream implements Stream { Future singleWhere(bool test(T element)) async => (await _stream.singleWhere(_validateType(test))) as T; - Future/**/ fold/**/(/*=S*/ initialValue, - /*=S*/ combine(/*=S*/ previous, T element)) => + Future fold(S initialValue, + S combine(S previous, T element)) => _stream.fold(initialValue, (previous, element) => combine(previous, element as T)); @@ -79,7 +79,7 @@ class TypeSafeStream implements Stream { new TypeSafeStreamSubscription(_stream.listen(_validateType(onData), onError: onError, onDone: onDone, cancelOnError: cancelOnError)); - Stream/**/ map/**/(/*=S*/ convert(T event)) => + Stream map(S convert(T event)) => _stream.map(_validateType(convert)); // Don't forward to `_stream.pipe` because we want the consumer to see the @@ -105,15 +105,15 @@ class TypeSafeStream implements Stream { onTimeout: (sink) => onTimeout(DelegatingEventSink.typed(sink)))); Future> toList() async => - DelegatingList.typed/**/(await _stream.toList()); + DelegatingList.typed(await _stream.toList()); Future> toSet() async => - DelegatingSet.typed/**/(await _stream.toSet()); + DelegatingSet.typed(await _stream.toSet()); // Don't forward to `_stream.transform` because we want the transformer to see // the type-asserted stream. - Stream/**/ transform/**/( - StreamTransformer transformer) => + Stream transform( + StreamTransformer transformer) => transformer.bind(this); Stream where(bool test(T element)) => @@ -132,7 +132,7 @@ class TypeSafeStream implements Stream { /// Returns a version of [function] that asserts that its argument is an /// instance of `T`. - UnaryFunction/**/ _validateType/**/( - /*=S*/ function(T value)) => + UnaryFunction _validateType( + S function(T value)) => function == null ? null : (value) => function(value as T); } diff --git a/pkgs/async/lib/src/typed/stream_subscription.dart b/pkgs/async/lib/src/typed/stream_subscription.dart index 2cd4ddfd..e02e1c0a 100644 --- a/pkgs/async/lib/src/typed/stream_subscription.dart +++ b/pkgs/async/lib/src/typed/stream_subscription.dart @@ -33,6 +33,6 @@ class TypeSafeStreamSubscription implements StreamSubscription { Future cancel() => _subscription.cancel(); - Future/**/ asFuture/**/([/*=E*/ futureValue]) => + Future asFuture([E futureValue]) => _subscription.asFuture(futureValue); } diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart index 2fb20c46..98d5e706 100644 --- a/pkgs/async/lib/src/typed_stream_transformer.dart +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -12,9 +12,9 @@ import 'delegate/stream.dart'; /// regardless of its original generic type, by asserting that the events /// emitted by the transformed stream are instances of `T` whenever they're /// provided. If they're not, the stream throws a [CastError]. -StreamTransformer/**/ typedStreamTransformer/**/( +StreamTransformer typedStreamTransformer( StreamTransformer transformer) => - transformer is StreamTransformer/**/ + transformer is StreamTransformer ? transformer : new _TypeSafeStreamTransformer(transformer); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 6fce524c..ea64ad72 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.0 +version: 1.13.1-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From ce177109109cb41c04eb234fe8b9124584e453be Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 Feb 2017 14:21:32 -0800 Subject: [PATCH 087/260] Use FutureOr. (dart-lang/async#27) Closes dart-lang/async#26 --- pkgs/async/CHANGELOG.md | 10 ++++------ pkgs/async/lib/src/async_memoizer.dart | 2 +- pkgs/async/lib/src/cancelable_operation.dart | 18 ++++++++++-------- pkgs/async/lib/src/delegate/future.dart | 2 +- pkgs/async/lib/src/lazy_stream.dart | 13 ++++++------- pkgs/async/lib/src/utils.dart | 5 +++++ pkgs/async/pubspec.yaml | 4 ++-- 7 files changed, 29 insertions(+), 25 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index be8377c1..2f62234e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,9 @@ +## 1.13.1 + +* Use `FutureOr` for various APIs that had previously used `dynamic`. + ## 1.13.0 -<<<<<<< ours * Add `collectBytes` and `collectBytesCancelable` functions which collects list-of-byte events into a single byte list. @@ -9,11 +12,6 @@ * `StreamQueue.withTransaction()` now properly returns whether or not the transaction was committed. -======= -* Add a `collectBytes` function which collects list-of-byte events into - a single byte list. -* Switched to using generic method syntax. ->>>>>>> theirs ## 1.12.0 diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index 0213f68d..d1c0f329 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -39,7 +39,7 @@ class AsyncMemoizer { /// Runs the function, [computation], if it hasn't been run before. /// /// If [runOnce] has already been called, this returns the original result. - Future runOnce(computation()) { + Future runOnce(FutureOr computation()) { if (!hasRun) _completer.complete(new Future.sync(computation)); return future; } diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index d0a1871f..a9663c10 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -6,6 +6,8 @@ import 'dart:async'; import 'package:async/async.dart'; +import 'utils.dart'; + /// An asynchronous operation that can be cancelled. /// /// The value of this operation is exposed as [value]. When this operation is @@ -22,13 +24,13 @@ class CancelableOperation { /// Creates a [CancelableOperation] wrapping [inner]. /// /// When this operation is canceled, [onCancel] will be called and any value - /// or error produced by [inner] will be discarded. The callback may return a - /// Future to indicate that asynchronous work has to be done to cancel the - /// future; this Future will be returned by [cancel]. + /// or error produced by [inner] will be discarded. If [onCancel] returns a + /// [Future], it will be forwarded to [cancel]. /// /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. - factory CancelableOperation.fromFuture(Future inner, {onCancel()}) { + factory CancelableOperation.fromFuture(Future inner, + {FutureOr onCancel()}) { var completer = new CancelableCompleter(onCancel: onCancel); completer.complete(inner); return completer.operation; @@ -86,17 +88,17 @@ class CancelableCompleter { final Completer _inner; /// The callback to call if the future is canceled. - final ZoneCallback _onCancel; + final FutureOrCallback _onCancel; /// Creates a new completer for a [CancelableOperation]. /// /// When the future operation canceled, as long as the completer hasn't yet - /// completed, [onCancel] is called. The callback may return a [Future]; if - /// so, that [Future] is returned by [CancelableOperation.cancel]. + /// completed, [onCancel] is called. If [onCancel] returns a [Future], it's + /// forwarded to [CancelableOperation.cancel]. /// /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. - CancelableCompleter({onCancel()}) + CancelableCompleter({FutureOr onCancel()}) : _onCancel = onCancel, _inner = new Completer() { _operation = new CancelableOperation._(this); diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 4704343d..f746190f 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -27,7 +27,7 @@ class DelegatingFuture implements Future { Future catchError(Function onError, {bool test(Object error)}) => _future.catchError(onError, test: test); - Future then(dynamic onValue(T value), {Function onError}) => + Future then(FutureOr onValue(T value), {Function onError}) => _future.then(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index ad00a039..d565b4dd 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -4,8 +4,9 @@ import "dart:async"; -import "stream_completer.dart"; import "delegate/stream.dart"; +import "stream_completer.dart"; +import "utils.dart"; /// A [Stream] wrapper that forwards to another [Stream] that's initialized /// lazily. @@ -15,13 +16,11 @@ import "delegate/stream.dart"; /// produce a `Stream`. class LazyStream extends Stream { /// The callback that's called to create the inner stream. - ZoneCallback _callback; + FutureOrCallback> _callback; /// Creates a single-subscription `Stream` that calls [callback] when it gets /// a listener and forwards to the returned stream. - /// - /// The [callback] may return a `Stream` or a `Future`. - LazyStream(callback()) : _callback = callback { + LazyStream(FutureOr> callback()) : _callback = callback { // Explicitly check for null because we null out [_callback] internally. if (_callback == null) throw new ArgumentError.notNull('callback'); } @@ -41,9 +40,9 @@ class LazyStream extends Stream { var result = callback(); Stream stream; - if (result is Future) { + if (result is Future>) { stream = StreamCompleter.fromFuture(result.then((stream) { - return DelegatingStream.typed(stream as Stream); + return DelegatingStream.typed(stream); })); } else { stream = DelegatingStream.typed(result as Stream); diff --git a/pkgs/async/lib/src/utils.dart b/pkgs/async/lib/src/utils.dart index 0066003d..2aab40e1 100644 --- a/pkgs/async/lib/src/utils.dart +++ b/pkgs/async/lib/src/utils.dart @@ -2,6 +2,11 @@ // 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. +import 'dart:async'; + /// A generic typedef for a function that takes one type and returns another. typedef F UnaryFunction(E argument); +/// A typedef for a function that takes no arguments and returns a Future or a +/// value. +typedef FutureOr FutureOrCallback(); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index ea64ad72..c6664349 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.1-dev +version: 1.13.1 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async @@ -10,4 +10,4 @@ dev_dependencies: stack_trace: "^1.0.0" test: "^0.12.0" environment: - sdk: ">=1.21.0 <2.0.0" + sdk: ">=1.22.0 <2.0.0" From 8bffaa4b700898b7be7c214854e81125ac75176b Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 20 Feb 2017 12:34:15 +0100 Subject: [PATCH 088/260] Fix type warning in Result.release. R=floitsch@google.com Review-Url: https://codereview.chromium.org//2707883002 . --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/result.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 2f62234e..6073ae44 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.13.2 + +* Fix a type-warning. + ## 1.13.1 * Use `FutureOr` for various APIs that had previously used `dynamic`. diff --git a/pkgs/async/lib/src/result.dart b/pkgs/async/lib/src/result.dart index 51db558c..7c45846e 100644 --- a/pkgs/async/lib/src/result.dart +++ b/pkgs/async/lib/src/result.dart @@ -90,7 +90,7 @@ abstract class Result { /// If [future] completes with an error, the returned future completes with /// the same error. static Future release(Future> future) => - future.then>((result) => result.asFuture); + future.then((result) => result.asFuture); /// Capture the results of a stream into a stream of [Result] values. /// diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index c6664349..adb2c82a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.1 +version: 1.13.2 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 4eb97cb6b2c25df8ca315c81c839e19a9a9ea40a Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 6 Mar 2017 10:45:56 -0800 Subject: [PATCH 089/260] Remove unused import in byte_collection_test.dart --- pkgs/async/test/byte_collection_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart index 74ab0435..e3d4f05a 100644 --- a/pkgs/async/test/byte_collection_test.dart +++ b/pkgs/async/test/byte_collection_test.dart @@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. import "dart:async"; -import "dart:typed_data"; import "package:test/test.dart"; import "package:async/async.dart"; From 5789617a761cc4670be8c724074229ec643ce1ff Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sat, 29 Apr 2017 11:45:00 -0700 Subject: [PATCH 090/260] dartfmt (with 1.23.x) --- pkgs/async/lib/src/async_cache.dart | 3 +- pkgs/async/lib/src/byte_collector.dart | 11 +- pkgs/async/lib/src/cancelable_operation.dart | 4 +- pkgs/async/lib/src/delegate/future.dart | 6 +- .../lib/src/delegate/stream_subscription.dart | 6 +- pkgs/async/lib/src/future_group.dart | 2 +- pkgs/async/lib/src/lazy_stream.dart | 4 +- pkgs/async/lib/src/result.dart | 6 +- pkgs/async/lib/src/result/future.dart | 3 +- .../src/single_subscription_transformer.dart | 4 +- pkgs/async/lib/src/stream_completer.dart | 22 +- pkgs/async/lib/src/stream_group.dart | 10 +- pkgs/async/lib/src/stream_queue.dart | 45 ++-- pkgs/async/lib/src/stream_sink_completer.dart | 6 +- .../lib/src/stream_sink_transformer.dart | 7 +- .../handler_transformer.dart | 3 +- .../stream_transformer_wrapper.dart | 21 +- pkgs/async/lib/src/stream_splitter.dart | 11 +- .../src/stream_subscription_transformer.dart | 36 +-- pkgs/async/lib/src/stream_zip.dart | 51 ++-- pkgs/async/lib/src/subscription_stream.dart | 6 +- pkgs/async/lib/src/typed/stream.dart | 33 ++- .../lib/src/typed/stream_subscription.dart | 3 +- pkgs/async/pubspec.yaml | 6 +- pkgs/async/test/async_cache_test.dart | 8 +- pkgs/async/test/byte_collection_test.dart | 6 +- .../async/test/cancelable_operation_test.dart | 37 +-- pkgs/async/test/restartable_timer_test.dart | 3 +- pkgs/async/test/result_test.dart | 151 ++++++------ .../single_subscription_transformer_test.dart | 8 +- pkgs/async/test/stream_completer_test.dart | 87 +++---- pkgs/async/test/stream_group_test.dart | 75 +++--- pkgs/async/test/stream_queue_test.dart | 87 ++++--- .../test/stream_sink_completer_test.dart | 8 +- .../test/stream_sink_transformer_test.dart | 73 +++--- pkgs/async/test/stream_splitter_test.dart | 30 ++- pkgs/async/test/stream_zip_test.dart | 220 ++++++++++++------ pkgs/async/test/stream_zip_zone_test.dart | 30 +-- pkgs/async/test/subscription_stream_test.dart | 36 ++- .../test/subscription_transformer_test.dart | 38 ++- .../async/test/typed_wrapper/future_test.dart | 34 +-- .../stream_subscription_test.dart | 8 +- .../async/test/typed_wrapper/stream_test.dart | 93 ++++---- pkgs/async/test/utils.dart | 25 +- 44 files changed, 722 insertions(+), 644 deletions(-) diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 96e1a50e..af52cd06 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -42,8 +42,7 @@ class AsyncCache { /// An ephemeral cache guarantees that a callback function will only be /// executed at most once concurrently. This is useful for requests for which /// data is updated frequently but stale data is acceptable. - factory AsyncCache.ephemeral() => - new AsyncCache(Duration.ZERO); + factory AsyncCache.ephemeral() => new AsyncCache(Duration.ZERO); /// Creates a cache that invalidates its contents after [duration] has passed. /// diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart index a8d2dbf4..3c1d49d2 100644 --- a/pkgs/async/lib/src/byte_collector.dart +++ b/pkgs/async/lib/src/byte_collector.dart @@ -29,9 +29,10 @@ Future collectBytes(Stream> source) { /// an eight-bit unsigned value in the resulting list. CancelableOperation collectBytesCancelable( Stream> source) { - return _collectBytes(source, (subscription, result) => - new CancelableOperation.fromFuture(result, onCancel: subscription.cancel) - ); + return _collectBytes( + source, + (subscription, result) => new CancelableOperation.fromFuture(result, + onCancel: subscription.cancel)); } /// Generalization over [collectBytes] and [collectBytesCancelable]. @@ -41,8 +42,8 @@ CancelableOperation collectBytesCancelable( /// so it can cancel the operation. T _collectBytes( Stream> source, - T result(StreamSubscription> subscription, - Future result)) { + T result( + StreamSubscription> subscription, Future result)) { var byteLists = >[]; var length = 0; var completer = new Completer.sync(); diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index a9663c10..2bf0f3b6 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -44,8 +44,8 @@ class CancelableOperation { /// This is like `value.asStream()`, but if a subscription to the stream is /// canceled, this is as well. Stream asStream() { - var controller = new StreamController( - sync: true, onCancel: _completer._cancel); + var controller = + new StreamController(sync: true, onCancel: _completer._cancel); value.then((value) { controller.add(value); diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index f746190f..129ad49d 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -25,13 +25,13 @@ class DelegatingFuture implements Future { Stream asStream() => _future.asStream(); Future catchError(Function onError, {bool test(Object error)}) => - _future.catchError(onError, test: test); + _future.catchError(onError, test: test); Future then(FutureOr onValue(T value), {Function onError}) => - _future.then(onValue, onError: onError); + _future.then(onValue, onError: onError); Future whenComplete(action()) => _future.whenComplete(action); Future timeout(Duration timeLimit, {onTimeout()}) => - _future.timeout(timeLimit, onTimeout: onTimeout); + _future.timeout(timeLimit, onTimeout: onTimeout); } diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 08234122..e7575d82 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -23,8 +23,7 @@ class DelegatingStreamSubscription implements StreamSubscription { /// regardless of its original generic type, by asserting that its events are /// instances of `T` whenever they're provided. If they're not, the /// subscription throws a [CastError]. - static StreamSubscription typed( - StreamSubscription subscription) => + static StreamSubscription typed(StreamSubscription subscription) => subscription is StreamSubscription ? subscription : new TypeSafeStreamSubscription(subscription); @@ -51,8 +50,7 @@ class DelegatingStreamSubscription implements StreamSubscription { Future cancel() => _source.cancel(); - Future asFuture([E futureValue]) => - _source.asFuture(futureValue); + Future asFuture([E futureValue]) => _source.asFuture(futureValue); bool get isPaused => _source.isPaused; } diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 9b85c2fc..0bf3158f 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -47,6 +47,7 @@ class FutureGroup implements Sink> { } return _onIdleController.stream; } + StreamController _onIdleController; /// The values emitted by the futures that have been added to the group, in @@ -93,4 +94,3 @@ class FutureGroup implements Sink> { _completer.complete(_values); } } - diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index d565b4dd..f07c387e 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -26,9 +26,7 @@ class LazyStream extends Stream { } StreamSubscription listen(void onData(T event), - {Function onError, - void onDone(), - bool cancelOnError}) { + {Function onError, void onDone(), bool cancelOnError}) { if (_callback == null) { throw new StateError("Stream has already been listened to."); } diff --git a/pkgs/async/lib/src/result.dart b/pkgs/async/lib/src/result.dart index 7c45846e..49457c35 100644 --- a/pkgs/async/lib/src/result.dart +++ b/pkgs/async/lib/src/result.dart @@ -78,8 +78,7 @@ abstract class Result { /// Errors have been converted to an [ErrorResult] value. static Future> capture(Future future) { return future.then((value) => new ValueResult(value), - onError: (error, stackTrace) => - new ErrorResult(error, stackTrace)); + onError: (error, stackTrace) => new ErrorResult(error, stackTrace)); } /// Release the result of a captured future. @@ -115,8 +114,7 @@ abstract class Result { /// result with the value is returned. static Result flatten(Result> result) { if (result.isValue) return result.asValue.value; - return new ErrorResult( - result.asError.error, result.asError.stackTrace); + return new ErrorResult(result.asError.error, result.asError.stackTrace); } /// Whether this result is a value result. diff --git a/pkgs/async/lib/src/result/future.dart b/pkgs/async/lib/src/result/future.dart index 209e8b1e..749b101f 100644 --- a/pkgs/async/lib/src/result/future.dart +++ b/pkgs/async/lib/src/result/future.dart @@ -29,6 +29,5 @@ class ResultFuture extends DelegatingFuture { return resultFuture; } - ResultFuture._(Future future) - : super(future); + ResultFuture._(Future future) : super(future); } diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index e01efac0..fcd6b063 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -18,8 +18,8 @@ class SingleSubscriptionTransformer implements StreamTransformer { Stream bind(Stream stream) { var subscription; - var controller = new StreamController(sync: true, - onCancel: () => subscription.cancel()); + var controller = new StreamController( + sync: true, onCancel: () => subscription.cancel()); subscription = stream.listen((value) { // TODO(nweiz): When we release a new major version, get rid of the second // type parameter and avoid this conversion. diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index 2bff6a5b..4311de52 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -36,8 +36,7 @@ class StreamCompleter { /// instead contain just that error. static Stream fromFuture(Future> streamFuture) { var completer = new StreamCompleter(); - streamFuture.then(completer.setSourceStream, - onError: completer.setError); + streamFuture.then(completer.setSourceStream, onError: completer.setError); return completer.stream; } @@ -118,23 +117,21 @@ class _CompleterStream extends Stream { Stream _sourceStream; StreamSubscription listen(onData(T data), - {Function onError, - void onDone(), - bool cancelOnError}) { + {Function onError, void onDone(), bool cancelOnError}) { if (_controller == null) { if (_sourceStream != null && !_sourceStream.isBroadcast) { // If the source stream is itself single subscription, // just listen to it directly instead of creating a controller. - return _sourceStream.listen(onData, onError: onError, onDone: onDone, - cancelOnError: cancelOnError); + return _sourceStream.listen(onData, + onError: onError, onDone: onDone, cancelOnError: cancelOnError); } _createController(); if (_sourceStream != null) { _linkStreamToController(); } } - return _controller.stream.listen(onData, onError: onError, onDone: onDone, - cancelOnError: cancelOnError); + return _controller.stream.listen(onData, + onError: onError, onDone: onDone, cancelOnError: cancelOnError); } /// Whether a source stream has been set. @@ -161,8 +158,9 @@ class _CompleterStream extends Stream { void _linkStreamToController() { assert(_controller != null); assert(_sourceStream != null); - _controller.addStream(_sourceStream, cancelOnError: false) - .whenComplete(_controller.close); + _controller + .addStream(_sourceStream, cancelOnError: false) + .whenComplete(_controller.close); } /// Sets an empty source stream. @@ -174,7 +172,7 @@ class _CompleterStream extends Stream { if (_controller == null) { _createController(); } - _sourceStream = _controller.stream; // Mark stream as set. + _sourceStream = _controller.stream; // Mark stream as set. _controller.close(); } diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 4aa448e3..6361a5cb 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -73,9 +73,7 @@ class StreamGroup implements Sink> { /// Creates a new stream group where [stream] is a broadcast stream. StreamGroup.broadcast() { _controller = new StreamController.broadcast( - onListen: _onListen, - onCancel: _onCancelBroadcast, - sync: true); + onListen: _onListen, onCancel: _onCancelBroadcast, sync: true); } /// Adds [stream] as a member of this group. @@ -193,10 +191,8 @@ class StreamGroup implements Sink> { /// /// This will pause the resulting subscription if [this] is paused. StreamSubscription _listenToStream(Stream stream) { - var subscription = stream.listen( - _controller.add, - onError: _controller.addError, - onDone: () => remove(stream)); + var subscription = stream.listen(_controller.add, + onError: _controller.addError, onDone: () => remove(stream)); if (_state == _StreamGroupState.paused) subscription.pause(); return subscription; } diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index f5780817..b9023ab9 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -131,7 +131,6 @@ abstract class StreamQueue { throw _failClosed(); } - /// Look at the next [count] data events without consuming them. /// /// Works like [take] except that the events are left in the queue. @@ -495,7 +494,6 @@ abstract class StreamQueue { } } - /// The default implementation of [StreamQueue]. /// /// This queue gets its events from a stream which is listened @@ -523,18 +521,14 @@ class _StreamQueue extends StreamQueue { void _ensureListening() { if (_isDone) return; if (_subscription == null) { - _subscription = - _sourceStream.listen( - (data) { - _addResult(new Result.value(data)); - }, - onError: (error, StackTrace stackTrace) { - _addResult(new Result.error(error, stackTrace)); - }, - onDone: () { - _subscription = null; - this._close(); - }); + _subscription = _sourceStream.listen((data) { + _addResult(new Result.value(data)); + }, onError: (error, StackTrace stackTrace) { + _addResult(new Result.error(error, stackTrace)); + }, onDone: () { + _subscription = null; + this._close(); + }); } else { _subscription.resume(); } @@ -650,8 +644,8 @@ class StreamQueueTransaction { queue._cancel(); } - assert((_parent._requestQueue.first as _TransactionRequest) - .transaction == this); + assert((_parent._requestQueue.first as _TransactionRequest).transaction == + this); _parent._requestQueue.removeFirst(); _parent._updateRequests(); } @@ -722,15 +716,14 @@ class _NextRequest implements _EventRequest { return true; } if (isDone) { - _completer.completeError(new StateError("No elements"), - StackTrace.current); + _completer.completeError( + new StateError("No elements"), StackTrace.current); return true; } return false; } } - /// Request for a [StreamQueue.peek] call. /// /// Completes the returned future when receiving the first event, @@ -749,15 +742,14 @@ class _PeekRequest implements _EventRequest { return true; } if (isDone) { - _completer.completeError(new StateError("No elements"), - StackTrace.current); + _completer.completeError( + new StateError("No elements"), StackTrace.current); return true; } return false; } } - /// Request for a [StreamQueue.skip] call. class _SkipRequest implements _EventRequest { /// Completer for the future returned by the skip call. @@ -815,7 +807,6 @@ abstract class _ListRequest implements _EventRequest { Future> get future => _completer.future; } - /// Request for a [StreamQueue.take] call. class _TakeRequest extends _ListRequest { _TakeRequest(int eventsToTake) : super(eventsToTake); @@ -839,7 +830,6 @@ class _TakeRequest extends _ListRequest { } } - /// Request for a [StreamQueue.lookAhead] call. class _LookAheadRequest extends _ListRequest { _LookAheadRequest(int eventsToTake) : super(eventsToTake); @@ -862,7 +852,6 @@ class _LookAheadRequest extends _ListRequest { } } - /// Request for a [StreamQueue.cancel] call. /// /// The request needs no events, it just waits in the request queue @@ -871,6 +860,7 @@ class _LookAheadRequest extends _ListRequest { class _CancelRequest implements _EventRequest { /// Completer for the future returned by the `cancel` call. final _completer = new Completer(); + /// /// When the event is completed, it needs to cancel the active subscription /// of the `StreamQueue` object, if any. @@ -926,8 +916,9 @@ class _RestRequest implements _EventRequest { for (var event in events) { event.addTo(controller); } - controller.addStream(_streamQueue._extractStream(), cancelOnError: false) - .whenComplete(controller.close); + controller + .addStream(_streamQueue._extractStream(), cancelOnError: false) + .whenComplete(controller.close); _completer.setSourceStream(controller.stream); } return true; diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index f8056c78..42191268 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -36,11 +36,9 @@ class StreamSinkCompleter { /// /// If the future completes with an error, the returned sink will instead /// be closed. Its [Sink.done] future will contain the error. - static StreamSink fromFuture( - Future> sinkFuture) { + static StreamSink fromFuture(Future> sinkFuture) { var completer = new StreamSinkCompleter(); - sinkFuture.then(completer.setDestinationSink, - onError: completer.setError); + sinkFuture.then(completer.setDestinationSink, onError: completer.setError); return completer.sink; } diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index 65adda67..503d28a7 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -24,8 +24,7 @@ abstract class StreamSinkTransformer { /// This is equivalent to piping all events from the outer sink through a /// stream transformed by [transformer] and from there into the inner sink. const factory StreamSinkTransformer.fromStreamTransformer( - StreamTransformer transformer) = - StreamTransformerWrapper; + StreamTransformer transformer) = StreamTransformerWrapper; /// Creates a [StreamSinkTransformer] that delegates events to the given /// handlers. @@ -34,8 +33,8 @@ abstract class StreamSinkTransformer { /// They're called for each incoming event, and any actions on the sink /// they're passed are forwarded to the inner sink. If a handler is omitted, /// the event is passed through unaltered. - factory StreamSinkTransformer.fromHandlers({ - void handleData(S data, EventSink sink), + factory StreamSinkTransformer.fromHandlers( + {void handleData(S data, EventSink sink), void handleError(Object error, StackTrace stackTrace, EventSink sink), void handleDone(EventSink sink)}) { return new HandlerTransformer(handleData, handleError, handleDone); diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index 8cc3d011..dba82408 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -28,8 +28,7 @@ class HandlerTransformer implements StreamSinkTransformer { /// The handler for done events. final HandleDone _handleDone; - HandlerTransformer( - this._handleData, this._handleError, this._handleDone); + HandlerTransformer(this._handleData, this._handleError, this._handleDone); StreamSink bind(StreamSink sink) => new _HandlerSink(this, sink); } diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart index 83d2b197..32ac648d 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -30,17 +30,16 @@ class _StreamTransformerWrapperSink implements StreamSink { Future get done => _inner.done; - _StreamTransformerWrapperSink(StreamTransformer transformer, - this._inner) { - _controller.stream.transform(transformer).listen( - _inner.add, - onError: _inner.addError, - onDone: () { - // Ignore any errors that come from this call to [_inner.close]. The - // user can access them through [done] or the value returned from - // [this.close], and we don't want them to get top-leveled. - _inner.close().catchError((_) {}); - }); + _StreamTransformerWrapperSink( + StreamTransformer transformer, this._inner) { + _controller.stream + .transform(transformer) + .listen(_inner.add, onError: _inner.addError, onDone: () { + // Ignore any errors that come from this call to [_inner.close]. The + // user can access them through [done] or the value returned from + // [this.close], and we don't want them to get top-leveled. + _inner.close().catchError((_) {}); + }); } void add(S event) { diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index 3d8f994e..6cec98c7 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -57,8 +57,7 @@ class StreamSplitter { /// /// [count] defaults to 2. This is the same as creating [count] branches and /// then closing the [StreamSplitter]. - static List> splitFrom(Stream stream, - [int count]) { + static List> splitFrom(Stream stream, [int count]) { if (count == null) count = 2; var splitter = new StreamSplitter(stream); var streams = new List.generate(count, (_) => splitter.split()); @@ -77,9 +76,7 @@ class StreamSplitter { } var controller = new StreamController( - onListen: _onListen, - onPause: _onPause, - onResume: _onResume); + onListen: _onListen, onPause: _onPause, onResume: _onResume); controller.onCancel = () => _onCancel(controller); for (var result in _buffer) { @@ -147,8 +144,8 @@ class StreamSplitter { // wasn't paused, this will be a no-op. _subscription.resume(); } else { - _subscription = _stream.listen( - _onData, onError: _onError, onDone: _onDone); + _subscription = + _stream.listen(_onData, onError: _onError, onDone: _onDone); } } diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart index ad545393..1443b183 100644 --- a/pkgs/async/lib/src/stream_subscription_transformer.dart +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -35,12 +35,14 @@ StreamTransformer subscriptionTransformer( return new _TransformedSubscription( stream.listen(null, cancelOnError: cancelOnError), handleCancel ?? (inner) => inner.cancel(), - handlePause ?? (inner) { - inner.pause(); - }, - handleResume ?? (inner) { - inner.resume(); - }); + handlePause ?? + (inner) { + inner.pause(); + }, + handleResume ?? + (inner) { + inner.resume(); + }); }); } @@ -61,8 +63,8 @@ class _TransformedSubscription implements StreamSubscription { bool get isPaused => _inner?.isPaused ?? false; - _TransformedSubscription(this._inner, this._handleCancel, this._handlePause, - this._handleResume); + _TransformedSubscription( + this._inner, this._handleCancel, this._handlePause, this._handleResume); void onData(void handleData(T data)) { _inner?.onData(handleData); @@ -77,15 +79,15 @@ class _TransformedSubscription implements StreamSubscription { } Future cancel() => _cancelMemoizer.runOnce(() { - var inner = _inner; - _inner.onData(null); - _inner.onDone(null); - - // Setting onError to null will cause errors to be top-leveled. - _inner.onError((_, __) {}); - _inner = null; - return _handleCancel(inner); - }); + var inner = _inner; + _inner.onData(null); + _inner.onDone(null); + + // Setting onError to null will cause errors to be top-leveled. + _inner.onError((_, __) {}); + _inner = null; + return _handleCancel(inner); + }); final _cancelMemoizer = new AsyncMemoizer(); void pause([Future resumeFuture]) { diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index a65840cb..3d5a8113 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -17,10 +17,8 @@ class StreamZip extends Stream> { StreamZip(Iterable> streams) : _streams = streams; - StreamSubscription> listen(void onData(List data), { - Function onError, - void onDone(), - bool cancelOnError}) { + StreamSubscription> listen(void onData(List data), + {Function onError, void onDone(), bool cancelOnError}) { cancelOnError = identical(true, cancelOnError); var subscriptions = >[]; StreamController> controller; @@ -72,8 +70,9 @@ class StreamZip extends Stream> { try { for (var stream in _streams) { int index = subscriptions.length; - subscriptions.add(stream.listen( - (data) { handleData(index, data); }, + subscriptions.add(stream.listen((data) { + handleData(index, data); + }, onError: cancelOnError ? handleError : handleErrorCancel, onDone: handleDone, cancelOnError: cancelOnError)); @@ -87,34 +86,28 @@ class StreamZip extends Stream> { current = new List(subscriptions.length); - controller = new StreamController>( - onPause: () { - for (int i = 0; i < subscriptions.length; i++) { - // This may pause some subscriptions more than once. - // These will not be resumed by onResume below, but must wait for the - // next round. - subscriptions[i].pause(); - } - }, - onResume: () { - for (int i = 0; i < subscriptions.length; i++) { - subscriptions[i].resume(); - } - }, - onCancel: () { - for (int i = 0; i < subscriptions.length; i++) { - // Canceling more than once is safe. - subscriptions[i].cancel(); - } + controller = new StreamController>(onPause: () { + for (int i = 0; i < subscriptions.length; i++) { + // This may pause some subscriptions more than once. + // These will not be resumed by onResume below, but must wait for the + // next round. + subscriptions[i].pause(); + } + }, onResume: () { + for (int i = 0; i < subscriptions.length; i++) { + subscriptions[i].resume(); + } + }, onCancel: () { + for (int i = 0; i < subscriptions.length; i++) { + // Canceling more than once is safe. + subscriptions[i].cancel(); } - ); + }); if (subscriptions.isEmpty) { controller.close(); } return controller.stream.listen(onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError); + onError: onError, onDone: onDone, cancelOnError: cancelOnError); } } diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 50ca81b6..a2356631 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -30,7 +30,7 @@ class SubscriptionStream extends Stream { /// stream. That may be an issue if `subscription` was made to cancel on /// an error. SubscriptionStream(StreamSubscription subscription) - : _source = subscription { + : _source = subscription { _source.pause(); // Clear callbacks to avoid keeping them alive unnecessarily. _source.onData(null); @@ -39,9 +39,7 @@ class SubscriptionStream extends Stream { } StreamSubscription listen(void onData(T event), - {Function onError, - void onDone(), - bool cancelOnError}) { + {Function onError, void onDone(), bool cancelOnError}) { if (_source == null) { throw new StateError("Stream has already been listened to."); } diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart index 235b205c..5b1f2ffb 100644 --- a/pkgs/async/lib/src/typed/stream.dart +++ b/pkgs/async/lib/src/typed/stream.dart @@ -30,11 +30,11 @@ class TypeSafeStream implements Stream { onListen: onListen == null ? null : (subscription) => - onListen(new TypeSafeStreamSubscription(subscription)), + onListen(new TypeSafeStreamSubscription(subscription)), onCancel: onCancel == null ? null : (subscription) => - onCancel(new TypeSafeStreamSubscription(subscription)))); + onCancel(new TypeSafeStreamSubscription(subscription)))); } Stream asyncExpand(Stream convert(T event)) => @@ -48,8 +48,7 @@ class TypeSafeStream implements Stream { ? null : (previous, next) => equals(previous as T, next as T))); - Future drain([E futureValue]) => - _stream.drain(futureValue); + Future drain([E futureValue]) => _stream.drain(futureValue); Stream expand(Iterable convert(T value)) => _stream.expand(_validateType(convert)); @@ -63,10 +62,9 @@ class TypeSafeStream implements Stream { Future singleWhere(bool test(T element)) async => (await _stream.singleWhere(_validateType(test))) as T; - Future fold(S initialValue, - S combine(S previous, T element)) => - _stream.fold(initialValue, - (previous, element) => combine(previous, element as T)); + Future fold(S initialValue, S combine(S previous, T element)) => + _stream.fold( + initialValue, (previous, element) => combine(previous, element as T)); Future forEach(void action(T element)) => _stream.forEach(_validateType(action)); @@ -79,8 +77,7 @@ class TypeSafeStream implements Stream { new TypeSafeStreamSubscription(_stream.listen(_validateType(onData), onError: onError, onDone: onDone, cancelOnError: cancelOnError)); - Stream map(S convert(T event)) => - _stream.map(_validateType(convert)); + Stream map(S convert(T event)) => _stream.map(_validateType(convert)); // Don't forward to `_stream.pipe` because we want the consumer to see the // type-asserted stream. @@ -88,8 +85,8 @@ class TypeSafeStream implements Stream { consumer.addStream(this).then((_) => consumer.close()); Future reduce(T combine(T previous, T element)) async { - var result = await _stream.reduce( - (previous, element) => combine(previous as T, element as T)); + var result = await _stream + .reduce((previous, element) => combine(previous as T, element as T)); return result as T; } @@ -100,20 +97,17 @@ class TypeSafeStream implements Stream { new TypeSafeStream(_stream.takeWhile(_validateType(test))); Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => - new TypeSafeStream(_stream.timeout( - timeLimit, + new TypeSafeStream(_stream.timeout(timeLimit, onTimeout: (sink) => onTimeout(DelegatingEventSink.typed(sink)))); Future> toList() async => DelegatingList.typed(await _stream.toList()); - Future> toSet() async => - DelegatingSet.typed(await _stream.toSet()); + Future> toSet() async => DelegatingSet.typed(await _stream.toSet()); // Don't forward to `_stream.transform` because we want the transformer to see // the type-asserted stream. - Stream transform( - StreamTransformer transformer) => + Stream transform(StreamTransformer transformer) => transformer.bind(this); Stream where(bool test(T element)) => @@ -132,7 +126,6 @@ class TypeSafeStream implements Stream { /// Returns a version of [function] that asserts that its argument is an /// instance of `T`. - UnaryFunction _validateType( - S function(T value)) => + UnaryFunction _validateType(S function(T value)) => function == null ? null : (value) => function(value as T); } diff --git a/pkgs/async/lib/src/typed/stream_subscription.dart b/pkgs/async/lib/src/typed/stream_subscription.dart index e02e1c0a..0fab0390 100644 --- a/pkgs/async/lib/src/typed/stream_subscription.dart +++ b/pkgs/async/lib/src/typed/stream_subscription.dart @@ -33,6 +33,5 @@ class TypeSafeStreamSubscription implements StreamSubscription { Future cancel() => _subscription.cancel(); - Future asFuture([E futureValue]) => - _subscription.asFuture(futureValue); + Future asFuture([E futureValue]) => _subscription.asFuture(futureValue); } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index adb2c82a..70c13e99 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,13 +1,13 @@ name: async -version: 1.13.2 +version: 1.13.3-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async +environment: + sdk: ">=1.22.0 <2.0.0" dependencies: collection: "^1.5.0" dev_dependencies: fake_async: "^0.1.2" stack_trace: "^1.0.0" test: "^0.12.0" -environment: - sdk: ">=1.22.0 <2.0.0" diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index b81d9f9f..747835b6 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -65,9 +65,11 @@ void main() { }); test('should fetch a stream via a callback', () async { - expect(await cache.fetchStream(expectAsync0(() { - return new Stream.fromIterable(['1', '2', '3']); - })).toList(), ['1', '2', '3']); + expect( + await cache.fetchStream(expectAsync0(() { + return new Stream.fromIterable(['1', '2', '3']); + })).toList(), + ['1', '2', '3']); }); test('should not fetch stream via callback when a cache exists', () async { diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart index e3d4f05a..b87b0741 100644 --- a/pkgs/async/test/byte_collection_test.dart +++ b/pkgs/async/test/byte_collection_test.dart @@ -67,7 +67,7 @@ void main() { var sc = new StreamController>(); var result = collectBytesCancelable(sc.stream); // Value never completes. - result.value.whenComplete(expectAsync0((){}, count: 0)); + result.value.whenComplete(expectAsync0(() {}, count: 0)); expect(sc.hasListener, isTrue); sc.add([1, 2]); @@ -77,7 +77,7 @@ void main() { await nextTimerTick(); expect(sc.hasListener, isTrue); result.cancel(); - expect(sc.hasListener, isFalse); // Cancelled immediately. + expect(sc.hasListener, isFalse); // Cancelled immediately. var replacement = await result.valueOrCancellation(); expect(replacement, isNull); await nextTimerTick(); @@ -87,4 +87,4 @@ void main() { }); } -Future nextTimerTick() => new Future((){}); +Future nextTimerTick() => new Future(() {}); diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index f30c134c..bded4025 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -13,8 +13,8 @@ void main() { group("without being canceled", () { var completer; setUp(() { - completer = new CancelableCompleter( - onCancel: expectAsync0(() {}, count: 0)); + completer = + new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); }); test("sends values to the future", () { @@ -74,8 +74,8 @@ void main() { test("successfully then with a future", () { completer.complete(1); - expect(() => completer.complete(new Completer().future), - throwsStateError); + expect( + () => completer.complete(new Completer().future), throwsStateError); }); test("with a future then successfully", () { @@ -85,8 +85,8 @@ void main() { test("with a future twice", () { completer.complete(new Completer().future); - expect(() => completer.complete(new Completer().future), - throwsStateError); + expect( + () => completer.complete(new Completer().future), throwsStateError); }); }); @@ -97,8 +97,8 @@ void main() { }); test("forwards errors", () { - var operation = new CancelableOperation.fromFuture( - new Future.error("error")); + var operation = + new CancelableOperation.fromFuture(new Future.error("error")); expect(operation.value, throwsA("error")); }); }); @@ -148,24 +148,26 @@ void main() { }); test("doesn't call onCancel if the completer has completed", () { - var completer = new CancelableCompleter( - onCancel: expectAsync0(() {}, count: 0)); + var completer = + new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); completer.complete(1); expect(completer.operation.value, completion(equals(1))); expect(completer.operation.cancel(), completes); }); - test("does call onCancel if the completer has completed to an unfired " + test( + "does call onCancel if the completer has completed to an unfired " "Future", () { var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); completer.complete(new Completer().future); expect(completer.operation.cancel(), completes); }); - test("doesn't call onCancel if the completer has completed to a fired " + test( + "doesn't call onCancel if the completer has completed to a fired " "Future", () async { - var completer = new CancelableCompleter( - onCancel: expectAsync0(() {}, count: 0)); + var completer = + new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); completer.complete(new Future.value(1)); await completer.operation.value; expect(completer.operation.cancel(), completes); @@ -195,7 +197,8 @@ void main() { test("valueOrCancellation waits on the onCancel future", () async { var innerCompleter = new Completer(); - var completer = new CancelableCompleter(onCancel: () => innerCompleter.future); + var completer = + new CancelableCompleter(onCancel: () => innerCompleter.future); var fired = false; completer.operation.valueOrCancellation().then((_) { @@ -229,8 +232,8 @@ void main() { test("cancels the completer when the subscription is canceled", () { var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); - var sub = completer.operation.asStream() - .listen(expectAsync1((_) {}, count: 0)); + var sub = + completer.operation.asStream().listen(expectAsync1((_) {}, count: 0)); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); sub.cancel(); expect(completer.isCanceled, isTrue); diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index b612f6e8..4f59f62c 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -100,8 +100,7 @@ main() { test("only runs the callback once if the timer isn't reset", () { new FakeAsync().run((async) { new RestartableTimer( - new Duration(seconds: 5), - expectAsync0(() {}, count: 1)); + new Duration(seconds: 5), expectAsync0(() {}, count: 1)); async.elapse(new Duration(seconds: 10)); }); }); diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result_test.dart index 21e251c6..210ae3fc 100644 --- a/pkgs/async/test/result_test.dart +++ b/pkgs/async/test/result_test.dart @@ -58,54 +58,60 @@ void main() { test("complete with value", () { Result result = new ValueResult(42); var c = new Completer(); - c.future.then(expectAsync1((int v) { expect(v, equals(42)); }), - onError: (e, s) { fail("Unexpected error"); }); + c.future.then(expectAsync1((int v) { + expect(v, equals(42)); + }), onError: (e, s) { + fail("Unexpected error"); + }); result.complete(c); }); test("complete with error", () { Result result = new ErrorResult("BAD", stack); var c = new Completer(); - c.future.then((bool v) { fail("Unexpected value $v"); }, - onError: expectAsync2((e, s) { - expect(e, equals("BAD")); - expect(s, same(stack)); - })); + c.future.then((bool v) { + fail("Unexpected value $v"); + }, onError: expectAsync2((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); result.complete(c); }); test("add sink value", () { var result = new ValueResult(42); - EventSink sink = new TestSink( - onData: expectAsync1((v) { expect(v, equals(42)); }) - ); + EventSink sink = new TestSink(onData: expectAsync1((v) { + expect(v, equals(42)); + })); result.addTo(sink); }); test("add sink error", () { Result result = new ErrorResult("BAD", stack); - EventSink sink = new TestSink( - onError: expectAsync2((e, s) { - expect(e, equals("BAD")); - expect(s, same(stack)); - }) - ); + EventSink sink = new TestSink(onError: expectAsync2((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); result.addTo(sink); }); test("value as future", () { Result result = new ValueResult(42); - result.asFuture.then(expectAsync1((int v) { expect(v, equals(42)); }), - onError: (e, s) { fail("Unexpected error"); }); + result.asFuture.then(expectAsync1((int v) { + expect(v, equals(42)); + }), onError: (e, s) { + fail("Unexpected error"); + }); }); test("error as future", () { Result result = new ErrorResult("BAD", stack); - result.asFuture.then((bool v) { fail("Unexpected value $v"); }, - onError: expectAsync2((e, s) { - expect(e, equals("BAD")); - expect(s, same(stack)); - })); + result.asFuture.then((bool v) { + fail("Unexpected value $v"); + }, onError: expectAsync2((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + })); }); test("capture future value", () { @@ -169,17 +175,19 @@ void main() { test("capture stream", () { StreamController c = new StreamController(); Stream stream = Result.captureStream(c.stream); - var expectedList = new Queue.from([new Result.value(42), - new Result.error("BAD", stack), - new Result.value(37)]); + var expectedList = new Queue.from([ + new Result.value(42), + new Result.error("BAD", stack), + new Result.value(37) + ]); void listener(Result actual) { expect(expectedList.isEmpty, isFalse); expectResult(actual, expectedList.removeFirst()); } - stream.listen(expectAsync1(listener, count: 3), - onError: (e, s) { fail("Unexpected error: $e"); }, - onDone: expectAsync0((){}), - cancelOnError: true); + + stream.listen(expectAsync1(listener, count: 3), onError: (e, s) { + fail("Unexpected error: $e"); + }, onDone: expectAsync0(() {}), cancelOnError: true); c.add(42); c.addError("BAD", stack); c.add(37); @@ -189,12 +197,14 @@ void main() { test("release stream", () { StreamController> c = new StreamController>(); Stream stream = Result.releaseStream(c.stream); - var events = [new Result.value(42), - new Result.error("BAD", stack), - new Result.value(37)]; + var events = [ + new Result.value(42), + new Result.error("BAD", stack), + new Result.value(37) + ]; // Expect the data events, and an extra error event. var expectedList = new Queue.from(events) - ..add(new Result.error("BAD2", stack)); + ..add(new Result.error("BAD2", stack)); void dataListener(int v) { expect(expectedList.isEmpty, isFalse); @@ -212,32 +222,32 @@ void main() { } stream.listen(expectAsync1(dataListener, count: 2), - onError: expectAsync2(errorListener, count: 2), - onDone: expectAsync0((){})); + onError: expectAsync2(errorListener, count: 2), + onDone: expectAsync0(() {})); for (Result result in events) { - c.add(result); // Result value or error in data line. + c.add(result); // Result value or error in data line. } - c.addError("BAD2", stack); // Error in error line. + c.addError("BAD2", stack); // Error in error line. c.close(); }); test("release stream cancel on error", () { StreamController> c = new StreamController>(); Stream stream = Result.releaseStream(c.stream); - stream.listen(expectAsync1((v) { expect(v, equals(42)); }), - onError: expectAsync2((e, s) { - expect(e, equals("BAD")); - expect(s, same(stack)); - }), - onDone: () { fail("Unexpected done event"); }, - cancelOnError: true); + stream.listen(expectAsync1((v) { + expect(v, equals(42)); + }), onError: expectAsync2((e, s) { + expect(e, equals("BAD")); + expect(s, same(stack)); + }), onDone: () { + fail("Unexpected done event"); + }, cancelOnError: true); c.add(new Result.value(42)); c.add(new Result.error("BAD", stack)); c.add(new Result.value(37)); c.close(); }); - test("flatten error 1", () { Result error = new Result.error("BAD", stack); Result flattened = @@ -292,18 +302,12 @@ void main() { test("handle neither unary nor binary", () { ErrorResult result = new Result.error("error", stack); - expect(() => result.handle(() => fail("unreachable")), - throws); - expect(() => result.handle((a, b, c) => fail("unreachable")), - throws); - expect(() => result.handle((a, b, {c}) => fail("unreachable")), - throws); - expect(() => result.handle((a, {b}) => fail("unreachable")), - throws); - expect(() => result.handle(({a, b}) => fail("unreachable")), - throws); - expect(() => result.handle(({a}) => fail("unreachable")), - throws); + expect(() => result.handle(() => fail("unreachable")), throws); + expect(() => result.handle((a, b, c) => fail("unreachable")), throws); + expect(() => result.handle((a, b, {c}) => fail("unreachable")), throws); + expect(() => result.handle((a, {b}) => fail("unreachable")), throws); + expect(() => result.handle(({a, b}) => fail("unreachable")), throws); + expect(() => result.handle(({a}) => fail("unreachable")), throws); }); } @@ -323,17 +327,32 @@ class TestSink implements EventSink { final Function onError; final Function onDone; - TestSink({void this.onData(T data) : _nullData, - void this.onError(e, StackTrace s) : _nullError, - void this.onDone() : _nullDone }); + TestSink( + {void this.onData(T data): _nullData, + void this.onError(e, StackTrace s): _nullError, + void this.onDone(): _nullDone}); - void add(T value) { onData(value); } - void addError(error, [StackTrace stack]) { onError(error, stack); } - void close() { onDone(); } + void add(T value) { + onData(value); + } + + void addError(error, [StackTrace stack]) { + onError(error, stack); + } + + void close() { + onDone(); + } + + static void _nullData(value) { + fail("Unexpected sink add: $value"); + } - static void _nullData(value) { fail("Unexpected sink add: $value"); } static void _nullError(e, StackTrace s) { fail("Unexpected sink addError: $e"); } - static void _nullDone() { fail("Unepxected sink close"); } + + static void _nullDone() { + fail("Unepxected sink close"); + } } diff --git a/pkgs/async/test/single_subscription_transformer_test.dart b/pkgs/async/test/single_subscription_transformer_test.dart index f21d4e9a..74e462b3 100644 --- a/pkgs/async/test/single_subscription_transformer_test.dart +++ b/pkgs/async/test/single_subscription_transformer_test.dart @@ -12,8 +12,8 @@ import 'utils.dart'; void main() { test("buffers events as soon as it's bound", () async { var controller = new StreamController.broadcast(); - var stream = controller.stream.transform( - const SingleSubscriptionTransformer()); + var stream = + controller.stream.transform(const SingleSubscriptionTransformer()); // Add events before [stream] has a listener to be sure it buffers them. controller.add(1); @@ -34,8 +34,8 @@ void main() { var controller = new StreamController.broadcast(onCancel: () { canceled = true; }); - var stream = controller.stream.transform( - const SingleSubscriptionTransformer()); + var stream = + controller.stream.transform(const SingleSubscriptionTransformer()); await flushMicrotasks(); expect(canceled, isFalse); diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index dea73d78..2e9ff9be 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -27,9 +27,9 @@ main() { test("cancel before linking a stream doesn't listen on stream", () async { var completer = new StreamCompleter(); var subscription = completer.stream.listen(null); - subscription.pause(); // Should be ignored. + subscription.pause(); // Should be ignored. subscription.cancel(); - completer.setSourceStream(new UnusableStream()); // Doesn't throw. + completer.setSourceStream(new UnusableStream()); // Doesn't throw. }); test("listen and pause before linking stream", () async { @@ -78,14 +78,13 @@ main() { var lastEvent = -1; var controller = new StreamController(); var subscription; - subscription = completer.stream.listen( - (value) { - expect(value, lessThan(3)); - lastEvent = value; - if (value == 2) { - subscription.cancel(); - } - }, + subscription = completer.stream.listen((value) { + expect(value, lessThan(3)); + lastEvent = value; + if (value == 2) { + subscription.cancel(); + } + }, onError: unreachable("error"), onDone: unreachable("done"), cancelOnError: true); @@ -110,20 +109,16 @@ main() { var completer = new StreamCompleter(); completer.setEmpty(); var done = new Completer(); - completer.stream.listen( - unreachable("data"), - onError: unreachable("error"), - onDone: done.complete); + completer.stream.listen(unreachable("data"), + onError: unreachable("error"), onDone: done.complete); await done.future; }); test("complete with setEmpty after listening", () async { var completer = new StreamCompleter(); var done = new Completer(); - completer.stream.listen( - unreachable("data"), - onError: unreachable("error"), - onDone: done.complete); + completer.stream.listen(unreachable("data"), + onError: unreachable("error"), onDone: done.complete); completer.setEmpty(); await done.future; }); @@ -147,17 +142,13 @@ main() { var completer = new StreamCompleter(); var lastEvent = -1; var controller = new StreamController(); - completer.stream.listen( - (value) { - expect(value, lessThan(3)); - lastEvent = value; - }, - onError: (value) { - expect(value, "3"); - lastEvent = value; - }, - onDone: unreachable("done"), - cancelOnError: true); + completer.stream.listen((value) { + expect(value, lessThan(3)); + lastEvent = value; + }, onError: (value) { + expect(value, "3"); + lastEvent = value; + }, onDone: unreachable("done"), cancelOnError: true); completer.setSourceStream(controller.stream); expect(controller.hasListener, isTrue); @@ -188,17 +179,13 @@ main() { controller.add(1); expect(controller.hasListener, isFalse); - completer.stream.listen( - (value) { - expect(value, lessThan(3)); - lastEvent = value; - }, - onError: (value) { - expect(value, "3"); - lastEvent = value; - }, - onDone: unreachable("done"), - cancelOnError: true); + completer.stream.listen((value) { + expect(value, lessThan(3)); + lastEvent = value; + }, onError: (value) { + expect(value, "3"); + lastEvent = value; + }, onDone: unreachable("done"), cancelOnError: true); expect(controller.hasListener, isTrue); @@ -324,8 +311,8 @@ main() { test("asFuture with error accross setting stream", () async { var completer = new StreamCompleter(); var controller = new StreamController(); - var subscription = completer.stream.listen(unreachable("data"), - cancelOnError: false); + var subscription = + completer.stream.listen(unreachable("data"), cancelOnError: false); var done = subscription.asFuture(); expect(controller.hasListener, isFalse); completer.setSourceStream(controller.stream); @@ -341,12 +328,10 @@ main() { group("setError()", () { test("produces a stream that emits a single error", () { var completer = new StreamCompleter(); - completer.stream.listen( - unreachable("data"), + completer.stream.listen(unreachable("data"), onError: expectAsync2((error, stackTrace) { - expect(error, equals("oh no")); - }), - onDone: expectAsync0(() {})); + expect(error, equals("oh no")); + }), onDone: expectAsync0(() {})); completer.setError("oh no"); }); @@ -357,12 +342,10 @@ main() { completer.setError("oh no"); await flushMicrotasks(); - completer.stream.listen( - unreachable("data"), + completer.stream.listen(unreachable("data"), onError: expectAsync2((error, stackTrace) { - expect(error, equals("oh no")); - }), - onDone: expectAsync0(() {})); + expect(error, equals("oh no")); + }), onDone: expectAsync0(() {})); }); }); } diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 078dc3c6..65ddb422 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -75,22 +75,24 @@ main() { new StreamTransformer.fromHandlers( handleData: (data, sink) => sink.add("data: $data"), handleError: (error, _, sink) => sink.add("error: $error"))); - expect(transformed.toList(), completion(equals([ - "data: first", - "error: second", - "data: third", - "error: fourth", - "error: fifth", - "data: sixth" - ]))); + expect( + transformed.toList(), + completion(equals([ + "data: first", + "error: second", + "data: third", + "error: fourth", + "error: fifth", + "data: sixth" + ]))); }); test("emits events once there's a listener", () { var controller = new StreamController(); streamGroup.add(controller.stream); - expect(streamGroup.stream.toList(), - completion(equals(["first", "second"]))); + expect( + streamGroup.stream.toList(), completion(equals(["first", "second"]))); controller.add("first"); controller.add("second"); @@ -137,8 +139,8 @@ main() { var controller = new StreamController.broadcast(); streamGroup.add(controller.stream); - expect(streamGroup.stream.toList(), - completion(equals(["first", "second"]))); + expect( + streamGroup.stream.toList(), completion(equals(["first", "second"]))); controller.add("first"); controller.add("second"); @@ -150,8 +152,8 @@ main() { test("forwards cancel errors", () async { var subscription = streamGroup.stream.listen(null); - var controller = new StreamController( - onCancel: () => throw "error"); + var controller = + new StreamController(onCancel: () => throw "error"); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -162,8 +164,8 @@ main() { var subscription = streamGroup.stream.listen(null); var completer = new Completer(); - var controller = new StreamController( - onCancel: () => completer.future); + var controller = + new StreamController(onCancel: () => completer.future); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -178,15 +180,15 @@ main() { expect(fired, isTrue); }); - test("add() while active pauses the stream if the group is paused, then " + test( + "add() while active pauses the stream if the group is paused, then " "resumes once the group resumes", () async { var subscription = streamGroup.stream.listen(null); await flushMicrotasks(); var paused = false; var controller = new StreamController( - onPause: () => paused = true, - onResume: () => paused = false); + onPause: () => paused = true, onResume: () => paused = false); subscription.pause(); await flushMicrotasks(); @@ -223,16 +225,16 @@ main() { }); test("forwards cancel errors", () { - var controller = new StreamController( - onCancel: () => throw "error"); + var controller = + new StreamController(onCancel: () => throw "error"); expect(streamGroup.add(controller.stream), throwsA("error")); }); test("forwards a cancel future", () async { var completer = new Completer(); - var controller = new StreamController( - onCancel: () => completer.future); + var controller = + new StreamController(onCancel: () => completer.future); var fired = false; streamGroup.add(controller.stream).then((_) => fired = true); @@ -268,8 +270,8 @@ main() { expect(streamGroup.close(), completes); - expect(streamGroup.stream.toList(), - completion(equals(["first", "second"]))); + expect( + streamGroup.stream.toList(), completion(equals(["first", "second"]))); }); test("emits events from multiple sources once there's a listener", () { @@ -279,8 +281,8 @@ main() { var controller2 = new StreamController(); streamGroup.add(controller2.stream); - expect(streamGroup.stream.toList(), - completion(equals(["first", "second"]))); + expect( + streamGroup.stream.toList(), completion(equals(["first", "second"]))); controller1.add("first"); controller2.add("second"); @@ -325,8 +327,8 @@ main() { var controller = new StreamController.broadcast(); streamGroup.add(controller.stream); - expect(streamGroup.stream.toList(), - completion(equals(["first", "second"]))); + expect( + streamGroup.stream.toList(), completion(equals(["first", "second"]))); controller.add("first"); controller.add("second"); @@ -356,8 +358,8 @@ main() { test("never cancels single-subscription streams", () async { var subscription = streamGroup.stream.listen(null); - var controller = new StreamController( - onCancel: expectAsync0(() {}, count: 0)); + var controller = + new StreamController(onCancel: expectAsync0(() {}, count: 0)); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -568,8 +570,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("forwards cancel errors", () async { - var controller = new StreamController( - onCancel: () => throw "error"); + var controller = + new StreamController(onCancel: () => throw "error"); streamGroup.add(controller.stream); streamGroup.stream.listen(null); @@ -580,8 +582,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { test("forwards cancel futures", () async { var completer = new Completer(); - var controller = new StreamController( - onCancel: () => completer.future); + var controller = + new StreamController(onCancel: () => completer.future); streamGroup.stream.listen(null); await flushMicrotasks(); @@ -641,7 +643,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(streamGroup.stream.toList(), completion(isEmpty)); }); - test("if there are streams, closes the group once those streams close " + test( + "if there are streams, closes the group once those streams close " "and there's a listener", () async { var controller1 = new StreamController(); var controller2 = new StreamController(); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index f487e654..2e4805b8 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -113,9 +113,9 @@ main() { test("with bad arguments throws", () async { var events = new StreamQueue(createStream()); expect(() => events.lookAhead(-1), throwsArgumentError); - expect(await events.next, 1); // Did not consume event. + expect(await events.next, 1); // Did not consume event. expect(() => events.lookAhead(-1), throwsArgumentError); - expect(await events.next, 2); // Did not consume event. + expect(await events.next, 2); // Did not consume event. await events.cancel(); }); @@ -153,8 +153,8 @@ main() { test("multiple requests at the same time", () async { var events = new StreamQueue(createStream()); - var result = await Future.wait( - [events.next, events.next, events.next, events.next]); + var result = await Future + .wait([events.next, events.next, events.next, events.next]); expect(result, [1, 2, 3, 4]); await events.cancel(); }); @@ -183,9 +183,9 @@ main() { expect(() => events.skip(-1), throwsArgumentError); // A non-int throws either a type error or an argument error, // depending on whether it's checked mode or not. - expect(await events.next, 1); // Did not consume event. + expect(await events.next, 1); // Did not consume event. expect(() => events.skip(-1), throwsArgumentError); - expect(await events.next, 2); // Did not consume event. + expect(await events.next, 2); // Did not consume event. await events.cancel(); }); @@ -251,14 +251,16 @@ main() { var index = 0; // Check that futures complete in order. Func1Required sequence(expectedValue, sequenceIndex) => (value) { - expect(value, expectedValue); - expect(index, sequenceIndex); - index++; - }; - await Future.wait([skip1.then(sequence(0, 0)), - skip2.then(sequence(0, 1)), - skip3.then(sequence(1, 2)), - skip4.then(sequence(1, 3))]); + expect(value, expectedValue); + expect(index, sequenceIndex); + index++; + }; + await Future.wait([ + skip1.then(sequence(0, 0)), + skip2.then(sequence(0, 1)), + skip3.then(sequence(1, 2)), + skip4.then(sequence(1, 3)) + ]); await events.cancel(); }); }); @@ -291,9 +293,9 @@ main() { test("with bad arguments throws", () async { var events = new StreamQueue(createStream()); expect(() => events.take(-1), throwsArgumentError); - expect(await events.next, 1); // Did not consume event. + expect(await events.next, 1); // Did not consume event. expect(() => events.take(-1), throwsArgumentError); - expect(await events.next, 2); // Did not consume event. + expect(await events.next, 2); // Did not consume event. await events.cancel(); }); @@ -522,7 +524,8 @@ main() { test("cancels the underlying subscription when called before any event", () async { var cancelFuture = new Future.value(42); - var controller = new StreamController(onCancel: () => cancelFuture); + var controller = + new StreamController(onCancel: () => cancelFuture); var events = new StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); @@ -539,8 +542,8 @@ main() { test("returns the result of closing the underlying subscription", () async { - var controller = new StreamController( - onCancel: () => new Future.value(42)); + var controller = + new StreamController(onCancel: () => new Future.value(42)); var events = new StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); }); @@ -548,8 +551,8 @@ main() { test("listens and then cancels a stream that hasn't been listened to yet", () async { var wasListened = false; - var controller = new StreamController( - onListen: () => wasListened = true); + var controller = + new StreamController(onListen: () => wasListened = true); var events = new StreamQueue(controller.stream); expect(wasListened, isFalse); expect(controller.hasListener, isFalse); @@ -608,7 +611,9 @@ main() { var events = new StreamQueue(controller.stream); var hasNext; - events.hasNext.then((result) { hasNext = result; }); + events.hasNext.then((result) { + hasNext = result; + }); await flushMicrotasks(); expect(hasNext, isNull); controller.add(42); @@ -622,7 +627,9 @@ main() { var events = new StreamQueue(controller.stream); var hasNext; - events.hasNext.then((result) { hasNext = result; }); + events.hasNext.then((result) { + hasNext = result; + }); await flushMicrotasks(); expect(hasNext, isNull); controller.addError("BAD"); @@ -976,7 +983,8 @@ main() { })); }); - test("the parent queue continues from the child position if it returns " + test( + "the parent queue continues from the child position if it returns " "true", () async { await events.withTransaction(expectAsync1((queue) async { expect(await queue.next, 2); @@ -986,7 +994,8 @@ main() { expect(await events.next, 3); }); - test("the parent queue continues from its original position if it returns " + test( + "the parent queue continues from its original position if it returns " "false", () async { await events.withTransaction(expectAsync1((queue) async { expect(await queue.next, 2); @@ -1036,12 +1045,15 @@ main() { expect(await events.next, 3); }); - test("the parent queue continues from the child position if an error is " + test( + "the parent queue continues from the child position if an error is " "thrown", () async { - expect(events.cancelable(expectAsync1((queue) async { - expect(await queue.next, 2); - throw "oh no"; - })).value, throwsA("oh no")); + expect( + events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + throw "oh no"; + })).value, + throwsA("oh no")); expect(events.next, completion(3)); }); @@ -1057,10 +1069,12 @@ main() { }); test("forwards the value from the callback", () async { - expect(await events.cancelable(expectAsync1((queue) async { - expect(await queue.next, 2); - return "value"; - })).value, "value"); + expect( + await events.cancelable(expectAsync1((queue) async { + expect(await queue.next, 2); + return "value"; + })).value, + "value"); }); }); @@ -1088,8 +1102,9 @@ main() { // `take(10)`. takeTest(startIndex) { expect(events.take(10), - completion(new List.generate(10, (i) => startIndex + i))); + completion(new List.generate(10, (i) => startIndex + i))); } + var tests = [nextTest, skipTest, takeTest]; int counter = 0; @@ -1103,7 +1118,7 @@ main() { } // Then expect 20 more events as a `rest` call. expect(events.rest.toList(), - completion(new List.generate(20, (i) => counter + i))); + completion(new List.generate(20, (i) => counter + i))); }); } diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index c7ce1ea6..3c8b576e 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -289,9 +289,9 @@ main() { test("doesn't allow the destination sink to be set multiple times", () { completer.setDestinationSink(new TestSink()); - expect(() => completer.setDestinationSink(new TestSink()), - throwsStateError); - expect(() => completer.setDestinationSink(new TestSink()), - throwsStateError); + expect( + () => completer.setDestinationSink(new TestSink()), throwsStateError); + expect( + () => completer.setDestinationSink(new TestSink()), throwsStateError); }); } diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index 6be9f9ee..208a03a7 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -19,8 +19,8 @@ void main() { test("transforms data events", () { var transformer = new StreamSinkTransformer.fromStreamTransformer( new StreamTransformer.fromHandlers(handleData: (i, sink) { - sink.add(i * 2); - })); + sink.add(i * 2); + })); var sink = transformer.bind(controller.sink); var results = []; @@ -38,18 +38,17 @@ void main() { var transformer = new StreamSinkTransformer.fromStreamTransformer( new StreamTransformer.fromHandlers( handleError: (i, stackTrace, sink) { - sink.addError((i as num) * 2, stackTrace); - })); + sink.addError((i as num) * 2, stackTrace); + })); var sink = transformer.bind(controller.sink); var results = []; controller.stream.listen(expectAsync1((_) {}, count: 0), onError: (error, stackTrace) { - results.add(error); - }, - onDone: expectAsync0(() { - expect(results, equals([2, 4, 6])); - })); + results.add(error); + }, onDone: expectAsync0(() { + expect(results, equals([2, 4, 6])); + })); sink.addError(1); sink.addError(2); @@ -59,11 +58,10 @@ void main() { test("transforms done events", () { var transformer = new StreamSinkTransformer.fromStreamTransformer( - new StreamTransformer.fromHandlers( - handleDone: (sink) { - sink.add(1); - sink.close(); - })); + new StreamTransformer.fromHandlers(handleDone: (sink) { + sink.add(1); + sink.close(); + })); var sink = transformer.bind(controller.sink); var results = []; @@ -90,7 +88,7 @@ void main() { expect(doneResult.isComplete, isFalse); expect(closeResult.isComplete, isFalse); - // Once the inner sink is completed, the futures should fire. + // Once the inner sink is completed, the futures should fire. innerSink.completer.complete(); await flushMicrotasks(); expect(doneResult.isComplete, isTrue); @@ -100,8 +98,8 @@ void main() { test("doesn't top-level the future from inner.close", () async { var transformer = new StreamSinkTransformer.fromStreamTransformer( new StreamTransformer.fromHandlers(handleData: (_, sink) { - sink.close(); - })); + sink.close(); + })); var innerSink = new CompleterStreamSink(); var sink = transformer.bind(innerSink); @@ -119,10 +117,10 @@ void main() { group("fromHandlers", () { test("transforms data events", () { - var transformer = new StreamSinkTransformer.fromHandlers( - handleData: (i, sink) { - sink.add(i * 2); - }); + var transformer = + new StreamSinkTransformer.fromHandlers(handleData: (i, sink) { + sink.add(i * 2); + }); var sink = transformer.bind(controller.sink); var results = []; @@ -139,18 +137,17 @@ void main() { test("transforms error events", () { var transformer = new StreamSinkTransformer.fromHandlers( handleError: (i, stackTrace, sink) { - sink.addError((i as num) * 2, stackTrace); - }); + sink.addError((i as num) * 2, stackTrace); + }); var sink = transformer.bind(controller.sink); var results = []; controller.stream.listen(expectAsync1((_) {}, count: 0), onError: (error, stackTrace) { - results.add(error); - }, - onDone: expectAsync0(() { - expect(results, equals([2, 4, 6])); - })); + results.add(error); + }, onDone: expectAsync0(() { + expect(results, equals([2, 4, 6])); + })); sink.addError(1); sink.addError(2); @@ -159,11 +156,11 @@ void main() { }); test("transforms done events", () { - var transformer = new StreamSinkTransformer.fromHandlers( - handleDone: (sink) { - sink.add(1); - sink.close(); - }); + var transformer = + new StreamSinkTransformer.fromHandlers(handleDone: (sink) { + sink.add(1); + sink.close(); + }); var sink = transformer.bind(controller.sink); var results = []; @@ -189,7 +186,7 @@ void main() { expect(doneResult.isComplete, isFalse); expect(closeResult.isComplete, isFalse); - // Once the inner sink is completed, the futures should fire. + // Once the inner sink is completed, the futures should fire. innerSink.completer.complete(); await flushMicrotasks(); expect(doneResult.isComplete, isTrue); @@ -197,10 +194,10 @@ void main() { }); test("doesn't top-level the future from inner.close", () async { - var transformer = new StreamSinkTransformer.fromHandlers( - handleData: (_, sink) { - sink.close(); - }); + var transformer = + new StreamSinkTransformer.fromHandlers(handleData: (_, sink) { + sink.close(); + }); var innerSink = new CompleterStreamSink(); var sink = transformer.bind(innerSink); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index c562305e..68a6b74c 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -47,11 +47,12 @@ main() { controller.close(); var count = 0; - branch.listen(expectAsync1((value) { - expect(count, anyOf(0, 2)); - expect(value, equals(count + 1)); - count++; - }, count: 2), onError: expectAsync1((error) { + branch.listen( + expectAsync1((value) { + expect(count, anyOf(0, 2)); + expect(value, equals(count + 1)); + count++; + }, count: 2), onError: expectAsync1((error) { expect(count, equals(1)); expect(error, equals("error")); count++; @@ -60,7 +61,8 @@ main() { })); }); - test("a branch that's created in the middle of a stream replays it", () async { + test("a branch that's created in the middle of a stream replays it", + () async { controller.add(1); controller.add(2); await flushMicrotasks(); @@ -104,12 +106,12 @@ main() { controller.add(1); controller.add(2); await flushMicrotasks(); - + var branch2 = splitter.split(); controller.add(3); controller.close(); await flushMicrotasks(); - + var branch3 = splitter.split(); splitter.close(); @@ -207,7 +209,8 @@ main() { expect(controller.isPaused, isFalse); }); - test("the source stream is canceled when it's closed after all branches have " + test( + "the source stream is canceled when it's closed after all branches have " "been canceled", () async { var branch1 = splitter.split(); var branch2 = splitter.split(); @@ -233,7 +236,8 @@ main() { expect(controller.hasListener, isFalse); }); - test("the source stream is canceled when all branches are canceled after it " + test( + "the source stream is canceled when all branches are canceled after it " "has been closed", () async { var branch1 = splitter.split(); var branch2 = splitter.split(); @@ -257,7 +261,8 @@ main() { expect(controller.hasListener, isFalse); }); - test("a splitter that's closed before any branches are added never listens " + test( + "a splitter that's closed before any branches are added never listens " "to the source stream", () { splitter.close(); @@ -265,7 +270,8 @@ main() { controller.stream.listen(null); }); - test("splitFrom splits a source stream into the designated number of " + test( + "splitFrom splits a source stream into the designated number of " "branches", () { var branches = StreamSplitter.splitFrom(controller.stream, 5); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 958d1f22..71d8eeef 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -45,40 +45,87 @@ main() { } test("Basic", () { - testZip([mks([1, 2, 3]), mks([4, 5, 6]), mks([7, 8, 9])], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + testZip([ + mks([1, 2, 3]), + mks([4, 5, 6]), + mks([7, 8, 9]) + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); }); test("Uneven length 1", () { - testZip([mks([1, 2, 3, 99, 100]), mks([4, 5, 6]), mks([7, 8, 9])], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + testZip([ + mks([1, 2, 3, 99, 100]), + mks([4, 5, 6]), + mks([7, 8, 9]) + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); }); test("Uneven length 2", () { - testZip([mks([1, 2, 3]), mks([4, 5, 6, 99, 100]), mks([7, 8, 9])], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + testZip([ + mks([1, 2, 3]), + mks([4, 5, 6, 99, 100]), + mks([7, 8, 9]) + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); }); test("Uneven length 3", () { - testZip([mks([1, 2, 3]), mks([4, 5, 6]), mks([7, 8, 9, 99, 100])], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + testZip([ + mks([1, 2, 3]), + mks([4, 5, 6]), + mks([7, 8, 9, 99, 100]) + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); }); test("Uneven length 4", () { - testZip([mks([1, 2, 3, 98]), mks([4, 5, 6]), mks([7, 8, 9, 99, 100])], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + testZip([ + mks([1, 2, 3, 98]), + mks([4, 5, 6]), + mks([7, 8, 9, 99, 100]) + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); }); test("Empty 1", () { - testZip([mks([]), mks([4, 5, 6]), mks([7, 8, 9])], []); + testZip([ + mks([]), + mks([4, 5, 6]), + mks([7, 8, 9]) + ], []); }); test("Empty 2", () { - testZip([mks([1, 2, 3]), mks([]), mks([7, 8, 9])], []); + testZip([ + mks([1, 2, 3]), + mks([]), + mks([7, 8, 9]) + ], []); }); test("Empty 3", () { - testZip([mks([1, 2, 3]), mks([4, 5, 6]), mks([])], []); + testZip([ + mks([1, 2, 3]), + mks([4, 5, 6]), + mks([]) + ], []); }); test("Empty source", () { @@ -86,57 +133,88 @@ main() { }); test("Single Source", () { - testZip([mks([1, 2, 3])], [[1], [2], [3]]); + testZip([ + mks([1, 2, 3]) + ], [ + [1], + [2], + [3] + ]); }); test("Other-streams", () { Stream st1 = mks([1, 2, 3, 4, 5, 6]).where((x) => x < 4); - Stream st2 = new Stream.periodic(const Duration(milliseconds: 5), - (x) => x + 4).take(3); + Stream st2 = + new Stream.periodic(const Duration(milliseconds: 5), (x) => x + 4) + .take(3); StreamController c = new StreamController.broadcast(); Stream st3 = c.stream; - testZip([st1, st2, st3], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); - c..add(7)..add(8)..add(9)..close(); + testZip([ + st1, + st2, + st3 + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); + c + ..add(7) + ..add(8) + ..add(9) + ..close(); }); test("Error 1", () { - expect(new StreamZip([streamError(mks([1, 2, 3]), 2, "BAD-1"), - mks([4, 5, 6]), - mks([7, 8, 9])]).toList(), - throwsA(equals("BAD-1"))); + expect( + new StreamZip([ + streamError(mks([1, 2, 3]), 2, "BAD-1"), + mks([4, 5, 6]), + mks([7, 8, 9]) + ]).toList(), + throwsA(equals("BAD-1"))); }); test("Error 2", () { - expect(new StreamZip([mks([1, 2, 3]), - streamError(mks([4, 5, 6]), 5, "BAD-2"), - mks([7, 8, 9])]).toList(), - throwsA(equals("BAD-2"))); + expect( + new StreamZip([ + mks([1, 2, 3]), + streamError(mks([4, 5, 6]), 5, "BAD-2"), + mks([7, 8, 9]) + ]).toList(), + throwsA(equals("BAD-2"))); }); test("Error 3", () { - expect(new StreamZip([mks([1, 2, 3]), - mks([4, 5, 6]), - streamError(mks([7, 8, 9]), 8, "BAD-3")]).toList(), - throwsA(equals("BAD-3"))); + expect( + new StreamZip([ + mks([1, 2, 3]), + mks([4, 5, 6]), + streamError(mks([7, 8, 9]), 8, "BAD-3") + ]).toList(), + throwsA(equals("BAD-3"))); }); test("Error at end", () { - expect(new StreamZip([mks([1, 2, 3]), - streamError(mks([4, 5, 6]), 6, "BAD-4"), - mks([7, 8, 9])]).toList(), - throwsA(equals("BAD-4"))); + expect( + new StreamZip([ + mks([1, 2, 3]), + streamError(mks([4, 5, 6]), 6, "BAD-4"), + mks([7, 8, 9]) + ]).toList(), + throwsA(equals("BAD-4"))); }); test("Error before first end", () { // StreamControllers' streams with no "close" called will never be done, // so the fourth event of the first stream is guaranteed to come first. - expect(new StreamZip( - [streamError(mks([1, 2, 3, 4]), 4, "BAD-5"), - (new StreamController()..add(4)..add(5)..add(6)).stream, - (new StreamController()..add(7)..add(8)..add(9)).stream] - ).toList(), - throwsA(equals("BAD-5"))); + expect( + new StreamZip([ + streamError(mks([1, 2, 3, 4]), 4, "BAD-5"), + (new StreamController()..add(4)..add(5)..add(6)).stream, + (new StreamController()..add(7)..add(8)..add(9)).stream + ]).toList(), + throwsA(equals("BAD-5"))); }); test("Error after first end", () { @@ -144,40 +222,43 @@ main() { controller..add(7)..add(8)..add(9); // Transformer that puts error into controller when one of the first two // streams have sent a done event. - StreamTransformer trans = new StreamTransformer.fromHandlers( - handleDone: (EventSink s) { - Timer.run(() { controller.addError("BAD-6"); }); + StreamTransformer trans = + new StreamTransformer.fromHandlers(handleDone: (EventSink s) { + Timer.run(() { + controller.addError("BAD-6"); + }); s.close(); }); - testZip([mks([1, 2, 3]).transform(trans), - mks([4, 5, 6]).transform(trans), - controller.stream], - [[1, 4, 7], [2, 5, 8], [3, 6, 9]]); + testZip([ + mks([1, 2, 3]).transform(trans), + mks([4, 5, 6]).transform(trans), + controller.stream + ], [ + [1, 4, 7], + [2, 5, 8], + [3, 6, 9] + ]); }); test("Pause/Resume", () { int sc1p = 0; - StreamController c1 = new StreamController( - onPause: () { - sc1p++; - }, - onResume: () { - sc1p--; - }); + StreamController c1 = new StreamController(onPause: () { + sc1p++; + }, onResume: () { + sc1p--; + }); int sc2p = 0; - StreamController c2 = new StreamController( - onPause: () { - sc2p++; - }, - onResume: () { - sc2p--; - }); + StreamController c2 = new StreamController(onPause: () { + sc2p++; + }, onResume: () { + sc2p--; + }); - var done = expectAsync0((){ + var done = expectAsync0(() { expect(sc1p, equals(1)); expect(sc2p, equals(0)); - }); // Call to complete test. + }); // Call to complete test. Stream zip = new StreamZip([c1.stream, c2.stream]); @@ -198,7 +279,9 @@ main() { }).then((hasMore) { expect(hasMore, isTrue); expect(it.current, equals([5, 6])); - new Future.delayed(ms25).then((_) { c2.add(8); }); + new Future.delayed(ms25).then((_) { + c2.add(8); + }); return it.moveNext(); }).then((hasMore) { expect(hasMore, isTrue); @@ -210,7 +293,12 @@ main() { done(); }); - c1..add(1)..add(3)..add(5)..add(7)..close(); + c1 + ..add(1) + ..add(3) + ..add(5) + ..add(7) + ..close(); c2..add(2)..add(4); }); diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index af4d94a4..a0773a68 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -9,22 +9,22 @@ import "package:test/test.dart"; // listen occurred. main() { - StreamController controller; - controller = new StreamController(); - testStream("singlesub-async", controller, controller.stream); - controller = new StreamController.broadcast(); - testStream("broadcast-async", controller, controller.stream); - controller = new StreamController(); - testStream("asbroadcast-async", controller, - controller.stream.asBroadcastStream()); + StreamController controller; + controller = new StreamController(); + testStream("singlesub-async", controller, controller.stream); + controller = new StreamController.broadcast(); + testStream("broadcast-async", controller, controller.stream); + controller = new StreamController(); + testStream( + "asbroadcast-async", controller, controller.stream.asBroadcastStream()); - controller = new StreamController(sync: true); - testStream("singlesub-sync", controller, controller.stream); - controller = new StreamController.broadcast(sync: true); - testStream("broadcast-sync", controller, controller.stream); - controller = new StreamController(sync: true); - testStream("asbroadcast-sync", controller, - controller.stream.asBroadcastStream()); + controller = new StreamController(sync: true); + testStream("singlesub-sync", controller, controller.stream); + controller = new StreamController.broadcast(sync: true); + testStream("broadcast-sync", controller, controller.stream); + controller = new StreamController(sync: true); + testStream( + "asbroadcast-sync", controller, controller.stream.asBroadcastStream()); } void testStream(String name, StreamController controller, Stream stream) { diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 699babee..6e2c9d5b 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -73,13 +73,13 @@ main() { for (var sourceCancels in [false, true]) { group("${sourceCancels ? "yes" : "no"}:", () { var subscriptionStream; - var onCancel; // Completes if source stream is canceled before done. + var onCancel; // Completes if source stream is canceled before done. setUp(() { var cancelCompleter = new Completer(); var source = createErrorStream(cancelCompleter); onCancel = cancelCompleter.future; - var sourceSubscription = source.listen(null, - cancelOnError: sourceCancels); + var sourceSubscription = + source.listen(null, cancelOnError: sourceCancels); subscriptionStream = new SubscriptionStream(sourceSubscription); }); @@ -87,15 +87,15 @@ main() { var done = new Completer(); var events = []; subscriptionStream.listen(events.add, - onError: events.add, - onDone: done.complete, - cancelOnError: false); + onError: events.add, onDone: done.complete, cancelOnError: false); var expected = [1, 2, "To err is divine!"]; if (sourceCancels) { await onCancel; // And [done] won't complete at all. bool isDone = false; - done.future.then((_) { isDone = true; }); + done.future.then((_) { + isDone = true; + }); await new Future.delayed(const Duration(milliseconds: 5)); expect(isDone, false); } else { @@ -109,12 +109,12 @@ main() { var completer = new Completer(); var events = []; subscriptionStream.listen(events.add, - onError: (value) { - events.add(value); - completer.complete(); - }, - onDone: () => throw "should not happen", - cancelOnError: true); + onError: (value) { + events.add(value); + completer.complete(); + }, + onDone: () => throw "should not happen", + cancelOnError: true); await completer.future; await flushMicrotasks(); expect(events, [1, 2, "To err is divine!"]); @@ -128,8 +128,7 @@ main() { var stream = createStream(); var sourceSubscription = stream.listen(null, cancelOnError: cancelOnError); - var subscriptionStream = - new SubscriptionStream(sourceSubscription); + var subscriptionStream = new SubscriptionStream(sourceSubscription); var subscription = subscriptionStream.listen(null, cancelOnError: cancelOnError); expect(subscription.asFuture(42), completion(42)); @@ -137,10 +136,9 @@ main() { test("- error goes to asFuture", () async { var stream = createErrorStream(); - var sourceSubscription = stream.listen(null, - cancelOnError: cancelOnError); - var subscriptionStream = - new SubscriptionStream(sourceSubscription); + var sourceSubscription = + stream.listen(null, cancelOnError: cancelOnError); + var subscriptionStream = new SubscriptionStream(sourceSubscription); var subscription = subscriptionStream.listen(null, cancelOnError: cancelOnError); diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index cdac4f8b..dbbf5971 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -86,13 +86,11 @@ void main() { var controller = new StreamController(onCancel: expectAsync0(() { isCanceled = true; })); - var subscription = controller.stream - .transform(subscriptionTransformer( - handleCancel: expectAsync1((inner) { - callbackInvoked = true; - inner.cancel(); - }))) - .listen(expectAsync1((_) {}, count: 0)); + var subscription = controller.stream.transform( + subscriptionTransformer(handleCancel: expectAsync1((inner) { + callbackInvoked = true; + inner.cancel(); + }))).listen(expectAsync1((_) {}, count: 0)); await flushMicrotasks(); expect(callbackInvoked, isFalse); @@ -103,7 +101,7 @@ void main() { expect(callbackInvoked, isTrue); expect(isCanceled, isTrue); }); - + test("invokes the callback once and caches its result", () async { var completer = new Completer(); var controller = new StreamController(); @@ -138,7 +136,8 @@ void main() { var pauseCount = 0; var controller = new StreamController(); var subscription = controller.stream - .transform(subscriptionTransformer(handlePause: expectAsync1((inner) { + .transform(subscriptionTransformer( + handlePause: expectAsync1((inner) { pauseCount++; inner.pause(); }, count: 3))) @@ -187,9 +186,9 @@ void main() { var subscription = controller.stream .transform(subscriptionTransformer( handleResume: expectAsync1((inner) { - resumeCount++; - inner.resume(); - }, count: 3))) + resumeCount++; + inner.resume(); + }, count: 3))) .listen(expectAsync1((_) {}, count: 0)); await flushMicrotasks(); @@ -216,13 +215,11 @@ void main() { test("invokes the callback when a resume future completes", () async { var resumed = false; var controller = new StreamController(); - var subscription = controller.stream - .transform(subscriptionTransformer( - handleResume: expectAsync1((inner) { - resumed = true; - inner.resume(); - }))) - .listen(expectAsync1((_) {}, count: 0)); + var subscription = controller.stream.transform( + subscriptionTransformer(handleResume: expectAsync1((inner) { + resumed = true; + inner.resume(); + }))).listen(expectAsync1((_) {}, count: 0)); var completer = new Completer(); subscription.pause(completer.future); @@ -255,8 +252,7 @@ void main() { var controller = new StreamController(); subscription = controller.stream .transform(subscriptionTransformer(handleCancel: (_) {})) - .listen( - expectAsync1((_) {}, count: 0), + .listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((_, __) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); subscription.cancel(); diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart index b928e054..0c8b00ac 100644 --- a/pkgs/async/test/typed_wrapper/future_test.dart +++ b/pkgs/async/test/typed_wrapper/future_test.dart @@ -32,20 +32,22 @@ void main() { test: expectAsync1((_) {}, count: 0)), completion(equals(12))); - expect(errorWrapper.catchError(expectAsync1((error) { - expect(error, equals("oh no")); - return 42; - }), test: expectAsync1((error) { - expect(error, equals("oh no")); - return true; - })), completion(equals(42))); + expect( + errorWrapper.catchError(expectAsync1((error) { + expect(error, equals("oh no")); + return 42; + }), test: expectAsync1((error) { + expect(error, equals("oh no")); + return true; + })), + completion(equals(42))); }); test("then()", () { - expect(wrapper.then((value) => value.toString()), - completion(equals("12"))); - expect(errorWrapper.then(expectAsync1((_) {}, count: 0)), - throwsA("oh no")); + expect( + wrapper.then((value) => value.toString()), completion(equals("12"))); + expect( + errorWrapper.then(expectAsync1((_) {}, count: 0)), throwsA("oh no")); }); test("whenComplete()", () { @@ -96,11 +98,11 @@ void main() { expect(wrapper.timeout(new Duration(seconds: 3)).then((_) {}), throwsCastError); - expect( - new TypeSafeFuture(new Completer().future) - .timeout(Duration.ZERO, onTimeout: expectAsync0(() => "foo")) - .then((_) {}), - throwsCastError); + expect( + new TypeSafeFuture(new Completer().future) + .timeout(Duration.ZERO, onTimeout: expectAsync0(() => "foo")) + .then((_) {}), + throwsCastError); }); }); }); diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index 07a8bd29..f52abe7e 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -18,8 +18,8 @@ void main() { controller = new StreamController(onCancel: () { isCanceled = true; }); - wrapper = new TypeSafeStreamSubscription( - controller.stream.listen(null)); + wrapper = + new TypeSafeStreamSubscription(controller.stream.listen(null)); }); test("onData()", () { @@ -75,8 +75,8 @@ void main() { controller = new StreamController(onCancel: () { isCanceled = true; }); - wrapper = new TypeSafeStreamSubscription( - controller.stream.listen(null)); + wrapper = + new TypeSafeStreamSubscription(controller.stream.listen(null)); }); group("throws a CastError for", () { diff --git a/pkgs/async/test/typed_wrapper/stream_test.dart b/pkgs/async/test/typed_wrapper/stream_test.dart index 12a130a3..88fbee65 100644 --- a/pkgs/async/test/typed_wrapper/stream_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_test.dart @@ -18,14 +18,19 @@ void main() { var errorWrapper; setUp(() { controller = new StreamController() - ..add(1)..add(2)..add(3)..add(4)..add(5)..close(); + ..add(1) + ..add(2) + ..add(3) + ..add(4) + ..add(5) + ..close(); // TODO(nweiz): Use public methods when test#414 is fixed and we can run // this on DDC. wrapper = new TypeSafeStream(controller.stream); emptyWrapper = new TypeSafeStream(new Stream.empty()); - singleWrapper = new TypeSafeStream( - new Stream.fromIterable([1])); + singleWrapper = + new TypeSafeStream(new Stream.fromIterable([1])); errorWrapper = new TypeSafeStream( new Stream.fromFuture(new Future.error("oh no"))); }); @@ -70,8 +75,8 @@ void main() { }); test("with onListen", () { - var broadcast = wrapper.asBroadcastStream( - onListen: expectAsync1((subscription) { + var broadcast = + wrapper.asBroadcastStream(onListen: expectAsync1((subscription) { expect(subscription, new isInstanceOf>()); subscription.pause(); })); @@ -81,8 +86,8 @@ void main() { }); test("with onCancel", () { - var broadcast = wrapper.asBroadcastStream( - onCancel: expectAsync1((subscription) { + var broadcast = + wrapper.asBroadcastStream(onCancel: expectAsync1((subscription) { expect(subscription, new isInstanceOf>()); subscription.pause(); })); @@ -105,13 +110,14 @@ void main() { group("distinct()", () { test("without equals", () { - expect(wrapper.distinct().toList(), - completion(equals([1, 2, 3, 4, 5]))); + expect( + wrapper.distinct().toList(), completion(equals([1, 2, 3, 4, 5]))); expect( new TypeSafeStream( new Stream.fromIterable([1, 1, 2, 2, 3, 3])) - .distinct().toList(), + .distinct() + .toList(), completion(equals([1, 2, 3]))); }); @@ -133,8 +139,7 @@ void main() { }); test("expand()", () { - expect( - wrapper.expand((i) => [i, i]).toList(), + expect(wrapper.expand((i) => [i, i]).toList(), completion(equals([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]))); }); @@ -197,26 +202,32 @@ void main() { group("handleError()", () { test("without a test", () { - expect(errorWrapper.handleError(expectAsync1((error) { - expect(error, equals("oh no")); - })).toList(), completion(isEmpty)); + expect( + errorWrapper.handleError(expectAsync1((error) { + expect(error, equals("oh no")); + })).toList(), + completion(isEmpty)); }); test("with a matching test", () { - expect(errorWrapper.handleError(expectAsync1((error) { - expect(error, equals("oh no")); - }), test: expectAsync1((error) { - expect(error, equals("oh no")); - return true; - })).toList(), completion(isEmpty)); + expect( + errorWrapper.handleError(expectAsync1((error) { + expect(error, equals("oh no")); + }), test: expectAsync1((error) { + expect(error, equals("oh no")); + return true; + })).toList(), + completion(isEmpty)); }); test("with a matching test", () { - expect(errorWrapper.handleError(expectAsync1((_) {}, count: 0), - test: expectAsync1((error) { - expect(error, equals("oh no")); - return false; - })).toList(), throwsA("oh no")); + expect( + errorWrapper.handleError(expectAsync1((_) {}, count: 0), + test: expectAsync1((error) { + expect(error, equals("oh no")); + return false; + })).toList(), + throwsA("oh no")); }); }); @@ -260,24 +271,23 @@ void main() { }); test("takeWhile()", () { - expect(wrapper.takeWhile((i) => i < 3).toList(), - completion(equals([1, 2]))); + expect( + wrapper.takeWhile((i) => i < 3).toList(), completion(equals([1, 2]))); }); test("toSet()", () { expect(wrapper.toSet(), completion(unorderedEquals([1, 2, 3, 4, 5]))); expect( new TypeSafeStream( - new Stream.fromIterable([1, 1, 2, 2, 3, 3])) - .toSet(), + new Stream.fromIterable([1, 1, 2, 2, 3, 3])).toSet(), completion(unorderedEquals([1, 2, 3]))); }); test("transform()", () { - var transformer = new StreamTransformer.fromHandlers( - handleData: (data, sink) { - sink.add(data.toString()); - }); + var transformer = new StreamTransformer.fromHandlers( + handleData: (data, sink) { + sink.add(data.toString()); + }); expect(wrapper.transform(transformer).toList(), completion(equals(["1", "2", "3", "4", "5"]))); @@ -385,8 +395,8 @@ void main() { setUp(() { wrapper = new TypeSafeStream( new Stream.fromIterable(["foo", "bar", "baz"])); - singleWrapper = new TypeSafeStream( - new Stream.fromIterable(["foo"])); + singleWrapper = + new TypeSafeStream(new Stream.fromIterable(["foo"])); }); group("throws a CastError for", () { @@ -439,8 +449,8 @@ void main() { }); test("lastWhere()", () { - expect(wrapper.lastWhere(expectAsync1((_) {}, count: 0)), - throwsCastError); + expect( + wrapper.lastWhere(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("singleWhere()", () { @@ -454,7 +464,8 @@ void main() { }); test("forEach()", () async { - expect(wrapper.forEach(expectAsync1((_) {}, count: 0)), throwsCastError); + expect( + wrapper.forEach(expectAsync1((_) {}, count: 0)), throwsCastError); }); test("handleError()", () { @@ -468,8 +479,8 @@ void main() { }); test("map()", () { - expect(wrapper.map(expectAsync1((_) {}, count: 0)).first, - throwsCastError); + expect( + wrapper.map(expectAsync1((_) {}, count: 0)).first, throwsCastError); }); test("reduce()", () { diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 8499eb24..92708868 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -17,23 +17,24 @@ typedef void OptionalArgAction([a, b]); /// /// Returns a function that fails the test if it is ever called. OptionalArgAction unreachable(String name) => - ([a, b]) => fail("Unreachable: $name"); + ([a, b]) => fail("Unreachable: $name"); // TODO(nweiz): Use the version of this in test when test#418 is fixed. /// A matcher that runs a callback in its own zone and asserts that that zone /// emits an error that matches [matcher]. Matcher throwsZoned(matcher) => predicate((callback) { - var firstError = true; - runZoned(callback, onError: expectAsync2((error, stackTrace) { - if (firstError) { - expect(error, matcher); - firstError = false; - } else { - registerException(error, stackTrace); - } - }, max: -1)); - return true; -}); + var firstError = true; + runZoned(callback, + onError: expectAsync2((error, stackTrace) { + if (firstError) { + expect(error, matcher); + firstError = false; + } else { + registerException(error, stackTrace); + } + }, max: -1)); + return true; + }); /// A matcher that runs a callback in its own zone and asserts that that zone /// emits a [CastError]. From 5337bc4987f2cf0fe499b37aa8534e3bd61c4892 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sat, 29 Apr 2017 11:45:11 -0700 Subject: [PATCH 091/260] Enable Travis-CI --- pkgs/async/.travis.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 pkgs/async/.travis.yml diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml new file mode 100644 index 00000000..662d2feb --- /dev/null +++ b/pkgs/async/.travis.yml @@ -0,0 +1,23 @@ +language: dart +sudo: false +dart: + - dev + - stable + - 1.23.0 + - 1.22.1 +cache: + directories: + - $HOME/.pub-cache +dart_task: + - test: --platform vm + # No parallelism on Firefox (-j 1) + # Causes flakiness – need to investigate + - test: --platform firefox -j 1 + - test: --platform dartium + install_dartium: true + - dartanalyzer + - dartfmt +matrix: + exclude: + - dart: 1.22.1 + dart_task: dartfmt From 2ccffd76639253fa1a1276d959bc2baef29067c6 Mon Sep 17 00:00:00 2001 From: Florian Loitsch Date: Tue, 16 May 2017 14:55:44 +0200 Subject: [PATCH 092/260] Make `TypeSafeStream` extend `Stream`. This makes it automatically compatible with future additions to `Stream`, like the `groupBy` method which will be added in SDK version 1.24. It inherits the default implementation, which will use the `listen` method of the `TypeSafeStream`, returning a `TypeSafeStreamSubscription`, and will just have a type check for each event before the listener even sees it. BUG= R=floitsch@google.com Review-Url: https://codereview.chromium.org//2872233002 . --- pkgs/async/CHANGELOG.md | 9 +++++++++ pkgs/async/lib/src/typed/stream.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 6073ae44..48ca38e9 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,12 @@ +## 1.13.3 + +* Make `TypeSafeStream` extend `Stream` instead of implementing it. + This ensures that new methods on `Stream` are automatically picked up, + they will go through the `listen` method which type-checks every event. +* Enable Travis integration. +* Format with dartfmt. +* Remove some unused imports. + ## 1.13.2 * Fix a type-warning. diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart index 5b1f2ffb..a4671158 100644 --- a/pkgs/async/lib/src/typed/stream.dart +++ b/pkgs/async/lib/src/typed/stream.dart @@ -10,7 +10,7 @@ import '../utils.dart'; import 'stream_subscription.dart'; import '../delegate/event_sink.dart'; -class TypeSafeStream implements Stream { +class TypeSafeStream extends Stream { final Stream _stream; Future get first async => (await _stream.first) as T; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 70c13e99..2ee2505a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.3-dev +version: 1.13.3 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From a53324b6ee40c9aab96f789791b5c953b6b8b510 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 17 May 2017 15:00:21 -0700 Subject: [PATCH 093/260] Tweak the changelog. (dart-lang/async#32) This removes entries that aren't relevant to downstream consumers. --- pkgs/async/CHANGELOG.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 48ca38e9..e4a86967 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,12 +1,9 @@ ## 1.13.3 -* Make `TypeSafeStream` extend `Stream` instead of implementing it. - This ensures that new methods on `Stream` are automatically picked up, - they will go through the `listen` method which type-checks every event. -* Enable Travis integration. -* Format with dartfmt. -* Remove some unused imports. - +* Make `TypeSafeStream` extend `Stream` instead of implementing it. This ensures + that new methods on `Stream` are automatically picked up, they will go through + the `listen` method which type-checks every event. + ## 1.13.2 * Fix a type-warning. From b20d38bcc56c0661fa064bd46c2f31ad65d4e871 Mon Sep 17 00:00:00 2001 From: Lasse Reichstein Holst Nielsen Date: Fri, 25 Aug 2017 12:47:19 +0200 Subject: [PATCH 094/260] Add methods to Result. Bump version to 2.0 and remove deprecated features. R=floitsch@google.com Review-Url: https://codereview.chromium.org//2996143002 . --- pkgs/async/CHANGELOG.md | 9 + pkgs/async/lib/async.dart | 4 +- pkgs/async/lib/result.dart | 13 - pkgs/async/lib/src/result.dart | 150 ------------ pkgs/async/lib/src/result/capture_sink.dart | 7 +- .../lib/src/result/capture_transformer.dart | 10 +- pkgs/async/lib/src/result/error.dart | 21 +- pkgs/async/lib/src/result/future.dart | 16 +- pkgs/async/lib/src/result/release_sink.dart | 12 +- .../lib/src/result/release_transformer.dart | 10 +- pkgs/async/lib/src/result/result.dart | 224 ++++++++++++++++++ pkgs/async/lib/src/result/value.dart | 10 +- pkgs/async/lib/src/stream_queue.dart | 4 +- pkgs/async/lib/src/stream_splitter.dart | 2 +- pkgs/async/lib/stream_zip.dart | 9 - pkgs/async/pubspec.yaml | 2 +- .../test/result/result_captureAll_test.dart | 187 +++++++++++++++ .../test/result/result_flattenAll_test.dart | 54 +++++ .../test/{ => result}/result_future_test.dart | 0 pkgs/async/test/{ => result}/result_test.dart | 0 20 files changed, 522 insertions(+), 222 deletions(-) delete mode 100644 pkgs/async/lib/result.dart delete mode 100644 pkgs/async/lib/src/result.dart create mode 100644 pkgs/async/lib/src/result/result.dart delete mode 100644 pkgs/async/lib/stream_zip.dart create mode 100644 pkgs/async/test/result/result_captureAll_test.dart create mode 100644 pkgs/async/test/result/result_flattenAll_test.dart rename pkgs/async/test/{ => result}/result_future_test.dart (100%) rename pkgs/async/test/{ => result}/result_test.dart (100%) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index e4a86967..a1e96832 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,12 @@ +## 2.0.0 +* Remove deprecated public `result.dart` and `stream_zip.dart` libraries and + deprecated classes `ReleaseStreamTransformer` and `CaptureStreamTransformer`. + +* Add `captureAll` and `flattenList` static methods to `Result`. + +* Change `ErrorResult` to not be generic and always be a `Result`. + That makes an error independent of the type of result it occurs instead of. + ## 1.13.3 * Make `TypeSafeStream` extend `Stream` instead of implementing it. This ensures diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index e0900326..25fc0fd1 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -17,11 +17,9 @@ export "src/future_group.dart"; export "src/lazy_stream.dart"; export "src/null_stream_sink.dart"; export "src/restartable_timer.dart"; -export "src/result.dart"; -export "src/result/capture_transformer.dart"; +export "src/result/result.dart"; export "src/result/error.dart"; export "src/result/future.dart"; -export "src/result/release_transformer.dart"; export "src/result/value.dart"; export "src/single_subscription_transformer.dart"; export "src/stream_completer.dart"; diff --git a/pkgs/async/lib/result.dart b/pkgs/async/lib/result.dart deleted file mode 100644 index 627f2008..00000000 --- a/pkgs/async/lib/result.dart +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -/// Import `async.dart` instead. -@Deprecated("Will be removed in async 2.0.0.") -library dart.pkg.async.results; - -export "src/result.dart"; -export "src/result/capture_transformer.dart"; -export "src/result/error.dart"; -export "src/result/release_transformer.dart"; -export "src/result/value.dart"; diff --git a/pkgs/async/lib/src/result.dart b/pkgs/async/lib/src/result.dart deleted file mode 100644 index 49457c35..00000000 --- a/pkgs/async/lib/src/result.dart +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (c) 2016, 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. - -import 'dart:async'; - -import 'result/capture_transformer.dart'; -import 'result/error.dart'; -import 'result/release_transformer.dart'; -import 'result/value.dart'; -import 'stream_sink_transformer.dart'; - -/// The result of a computation. -/// -/// Capturing a result (either a returned value or a thrown error) means -/// converting it into a [Result] - either a [ValueResult] or an [ErrorResult]. -/// -/// This value can release itself by writing itself either to a [EventSink] or a -/// [Completer], or by becoming a [Future]. -abstract class Result { - /// A stream transformer that captures a stream of events into [Result]s. - /// - /// The result of the transformation is a stream of [Result] values and no - /// error events. This is the transformer used by [captureStream]. - static const StreamTransformer captureStreamTransformer = - const CaptureStreamTransformer(); - - /// A stream transformer that releases a stream of result events. - /// - /// The result of the transformation is a stream of values and error events. - /// This is the transformer used by [releaseStream]. - static const StreamTransformer releaseStreamTransformer = - const ReleaseStreamTransformer(); - - /// A sink transformer that captures events into [Result]s. - /// - /// The result of the transformation is a sink that only forwards [Result] - /// values and no error events. - static const StreamSinkTransformer captureSinkTransformer = - const StreamSinkTransformer.fromStreamTransformer( - const CaptureStreamTransformer()); - - /// A sink transformer that releases result events. - /// - /// The result of the transformation is a sink that forwards of values and - /// error events. - static const StreamSinkTransformer releaseSinkTransformer = - const StreamSinkTransformer.fromStreamTransformer( - const ReleaseStreamTransformer()); - - /// Create a `Result` with the result of calling [computation]. - /// - /// This generates either a [ValueResult] with the value returned by - /// calling `computation`, or an [ErrorResult] with an error thrown by - /// the call. - factory Result(T computation()) { - try { - return new ValueResult(computation()); - } catch (e, s) { - return new ErrorResult(e, s); - } - } - - /// Create a `Result` holding a value. - /// - /// Alias for [ValueResult.ValueResult]. - factory Result.value(T value) = ValueResult; - - /// Create a `Result` holding an error. - /// - /// Alias for [ErrorResult.ErrorResult]. - factory Result.error(Object error, [StackTrace stackTrace]) => - new ErrorResult(error, stackTrace); - - /// Capture the result of a future into a `Result` future. - /// - /// The resulting future will never have an error. - /// Errors have been converted to an [ErrorResult] value. - static Future> capture(Future future) { - return future.then((value) => new ValueResult(value), - onError: (error, stackTrace) => new ErrorResult(error, stackTrace)); - } - - /// Release the result of a captured future. - /// - /// Converts the [Result] value of the given [future] to a value or error - /// completion of the returned future. - /// - /// If [future] completes with an error, the returned future completes with - /// the same error. - static Future release(Future> future) => - future.then((result) => result.asFuture); - - /// Capture the results of a stream into a stream of [Result] values. - /// - /// The returned stream will not have any error events. - /// Errors from the source stream have been converted to [ErrorResult]s. - static Stream> captureStream(Stream source) => - source.transform(new CaptureStreamTransformer()); - - /// Release a stream of [result] values into a stream of the results. - /// - /// `Result` values of the source stream become value or error events in - /// the returned stream as appropriate. - /// Errors from the source stream become errors in the returned stream. - static Stream releaseStream(Stream> source) => - source.transform(new ReleaseStreamTransformer()); - - /// Converts a result of a result to a single result. - /// - /// If the result is an error, or it is a `Result` value - /// which is then an error, then a result with that error is returned. - /// Otherwise both levels of results are value results, and a single - /// result with the value is returned. - static Result flatten(Result> result) { - if (result.isValue) return result.asValue.value; - return new ErrorResult(result.asError.error, result.asError.stackTrace); - } - - /// Whether this result is a value result. - /// - /// Always the opposite of [isError]. - bool get isValue; - - /// Whether this result is an error result. - /// - /// Always the opposite of [isValue]. - bool get isError; - - /// If this is a value result, return itself. - /// - /// Otherwise return `null`. - ValueResult get asValue; - - /// If this is an error result, return itself. - /// - /// Otherwise return `null`. - ErrorResult get asError; - - /// Complete a completer with this result. - void complete(Completer completer); - - /// Add this result to an [EventSink]. - /// - /// Calls the sink's `add` or `addError` method as appropriate. - void addTo(EventSink sink); - - /// Creates a future completed with this result as a value or an error. - Future get asFuture; -} diff --git a/pkgs/async/lib/src/result/capture_sink.dart b/pkgs/async/lib/src/result/capture_sink.dart index 74745251..c85b5532 100644 --- a/pkgs/async/lib/src/result/capture_sink.dart +++ b/pkgs/async/lib/src/result/capture_sink.dart @@ -4,12 +4,11 @@ import 'dart:async'; -import '../result.dart'; +import 'result.dart'; -/// Use [Result.captureSinkTransformer]. -@Deprecated("Will be removed in async 2.0.0.") +/// Used by [Result.captureSink]. class CaptureSink implements EventSink { - final EventSink _sink; + final EventSink> _sink; CaptureSink(EventSink> sink) : _sink = sink; diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index 6b1bbf23..71e8a002 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -4,14 +4,13 @@ import 'dart:async'; -import '../result.dart'; +import 'result.dart'; import 'capture_sink.dart'; /// A stream transformer that captures a stream of events into [Result]s. /// /// The result of the transformation is a stream of [Result] values and no -/// error events. This is the transformer used by [captureStream]. -@Deprecated("Will be removed in async 2.0.0.") +/// error events. Exposed by [Result.captureStream]. class CaptureStreamTransformer implements StreamTransformer> { const CaptureStreamTransformer(); @@ -19,7 +18,6 @@ class CaptureStreamTransformer implements StreamTransformer> { return new Stream>.eventTransformed(source, _createSink); } - static EventSink _createSink(EventSink sink) { - return new CaptureSink(sink); - } + // Since Stream.eventTransformed is not generic, this method can be static. + static EventSink _createSink(EventSink sink) => new CaptureSink(sink); } diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index b6d58593..45e06a20 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -4,18 +4,21 @@ import 'dart:async'; -import '../result.dart'; +import 'result.dart'; import 'value.dart'; /// A result representing a thrown error. -class ErrorResult implements Result { +class ErrorResult implements Result { + /// The error object that was thrown. final error; + + /// The stack trace corresponding to where [error] was thrown. final StackTrace stackTrace; bool get isValue => false; bool get isError => true; - ValueResult get asValue => null; - ErrorResult get asError => this; + ValueResult get asValue => null; + ErrorResult get asError => this; ErrorResult(this.error, this.stackTrace); @@ -27,7 +30,7 @@ class ErrorResult implements Result { sink.addError(error, stackTrace); } - Future get asFuture => new Future.error(error, stackTrace); + Future get asFuture => new Future.error(error, stackTrace); /// Calls an error handler with the error and stacktrace. /// @@ -42,4 +45,12 @@ class ErrorResult implements Result { errorHandler(error); } } + + int get hashCode => error.hashCode ^ stackTrace.hashCode ^ 0x1d61823f; + + /// This is equal only to an error result with equal [error] and [stackTrace]. + bool operator ==(Object other) => + other is ErrorResult && + error == other.error && + stackTrace == other.stackTrace; } diff --git a/pkgs/async/lib/src/result/future.dart b/pkgs/async/lib/src/result/future.dart index 749b101f..ff305460 100644 --- a/pkgs/async/lib/src/result/future.dart +++ b/pkgs/async/lib/src/result/future.dart @@ -5,7 +5,7 @@ import 'dart:async'; import '../delegate/future.dart'; -import '../result.dart'; +import 'result.dart'; /// A [Future] wrapper that provides synchronous access to the result of the /// wrapped [Future] once it's completed. @@ -19,15 +19,9 @@ class ResultFuture extends DelegatingFuture { Result get result => _result; Result _result; - factory ResultFuture(Future future) { - ResultFuture resultFuture; - resultFuture = new ResultFuture._(() async { - var result = await Result.capture(future); - resultFuture._result = result; - return await result.asFuture; - }()); - return resultFuture; + ResultFuture(Future future) : super(future) { + Result.capture(future).then((result) { + _result = result; + }); } - - ResultFuture._(Future future) : super(future); } diff --git a/pkgs/async/lib/src/result/release_sink.dart b/pkgs/async/lib/src/result/release_sink.dart index 114981b6..a0715a17 100644 --- a/pkgs/async/lib/src/result/release_sink.dart +++ b/pkgs/async/lib/src/result/release_sink.dart @@ -4,22 +4,16 @@ import 'dart:async'; -import '../result.dart'; +import 'result.dart'; -/// Use [Result.captureSinkTransformer]. -@Deprecated("Will be removed in async 2.0.0.") +/// Used by [Result.releaseSink]. class ReleaseSink implements EventSink> { final EventSink _sink; ReleaseSink(EventSink sink) : _sink = sink; void add(Result result) { - if (result.isValue) { - _sink.add(result.asValue.value); - } else { - var error = result.asError; - _sink.addError(error.error, error.stackTrace); - } + result.addTo(_sink); } void addError(Object error, [StackTrace stackTrace]) { diff --git a/pkgs/async/lib/src/result/release_transformer.dart b/pkgs/async/lib/src/result/release_transformer.dart index 456ed0a9..fc9278d4 100644 --- a/pkgs/async/lib/src/result/release_transformer.dart +++ b/pkgs/async/lib/src/result/release_transformer.dart @@ -4,11 +4,10 @@ import 'dart:async'; -import '../result.dart'; +import 'result.dart'; import 'release_sink.dart'; -/// Use [Result.releaseTransformer] instead. -@Deprecated("Will be removed in async 2.0.0.") +/// A transformer that releases result events as data and error events. class ReleaseStreamTransformer implements StreamTransformer, T> { const ReleaseStreamTransformer(); @@ -16,7 +15,6 @@ class ReleaseStreamTransformer implements StreamTransformer, T> { return new Stream.eventTransformed(source, _createSink); } - static EventSink _createSink(EventSink sink) { - return new ReleaseSink(sink); - } + // Since Stream.eventTransformed is not generic, this method can be static. + static EventSink _createSink(EventSink sink) => new ReleaseSink(sink); } diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart new file mode 100644 index 00000000..98b5aa43 --- /dev/null +++ b/pkgs/async/lib/src/result/result.dart @@ -0,0 +1,224 @@ +// Copyright (c) 2016, 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. + +import 'dart:async'; + +import 'capture_sink.dart'; +import 'capture_transformer.dart'; +import 'error.dart'; +import 'release_sink.dart'; +import 'release_transformer.dart'; +import 'value.dart'; +import '../stream_sink_transformer.dart'; + +/// The result of a computation. +/// +/// Capturing a result (either a returned value or a thrown error) means +/// converting it into a [Result] - either a [ValueResult] or an [ErrorResult]. +/// +/// This value can release itself by writing itself either to a [EventSink] or a +/// [Completer], or by becoming a [Future]. +/// +/// A [Future] represents a potential result, one that might not have been +/// computed yet, and a [Result] is always a completed and available result. +abstract class Result { + /// A stream transformer that captures a stream of events into [Result]s. + /// + /// The result of the transformation is a stream of [Result] values and no + /// error events. This is the transformer used by [captureStream]. + static const StreamTransformer> + captureStreamTransformer = const CaptureStreamTransformer(); + + /// A stream transformer that releases a stream of result events. + /// + /// The result of the transformation is a stream of values and error events. + /// This is the transformer used by [releaseStream]. + static const StreamTransformer, Object> + releaseStreamTransformer = const ReleaseStreamTransformer(); + + /// A sink transformer that captures events into [Result]s. + /// + /// The result of the transformation is a sink that only forwards [Result] + /// values and no error events. + static const StreamSinkTransformer> + captureSinkTransformer = + const StreamSinkTransformer>.fromStreamTransformer( + const CaptureStreamTransformer()); + + /// A sink transformer that releases result events. + /// + /// The result of the transformation is a sink that forwards of values and + /// error events. + static const StreamSinkTransformer, Object> + releaseSinkTransformer = + const StreamSinkTransformer, Object>.fromStreamTransformer( + const ReleaseStreamTransformer()); + + /// Creates a `Result` with the result of calling [computation]. + /// + /// This generates either a [ValueResult] with the value returned by + /// calling `computation`, or an [ErrorResult] with an error thrown by + /// the call. + factory Result(T computation()) { + try { + return new ValueResult(computation()); + } catch (e, s) { + return new ErrorResult(e, s); + } + } + + /// Creates a `Result` holding a value. + /// + /// Alias for [ValueResult.ValueResult]. + factory Result.value(T value) = ValueResult; + + /// Creates a `Result` holding an error. + /// + /// Alias for [ErrorResult.ErrorResult]. + factory Result.error(Object error, [StackTrace stackTrace]) => + new ErrorResult(error, stackTrace); + + /// Captures the result of a future into a `Result` future. + /// + /// The resulting future will never have an error. + /// Errors have been converted to an [ErrorResult] value. + static Future> capture(Future future) { + return future.then((value) => new ValueResult(value), + onError: (error, stackTrace) => new ErrorResult(error, stackTrace)); + } + + /// Captures each future in [elements], + /// + /// Returns a (future of) a list of results for each element in [elements], + /// in iteration order. + /// Each future in [elements] is [capture]d and each non-future is + /// wrapped as a [Result.value]. + /// The returned future will never have an error. + static Future>> captureAll(Iterable> elements) { + var results = >[]; + int pending = 0; + var completer; + for (var element in elements) { + if (element is Future) { + int i = results.length; + results.add(null); + pending++; + Result.capture(element).then((result) { + results[i] = result; + if (--pending == 0) { + completer.complete(results); + } + }); + } else { + results.add(new Result.value(element)); + } + } + if (pending == 0) { + return new Future>>.value(results); + } + completer = new Completer>>(); + return completer.future; + } + + /// Releases the result of a captured future. + /// + /// Converts the [Result] value of the given [future] to a value or error + /// completion of the returned future. + /// + /// If [future] completes with an error, the returned future completes with + /// the same error. + static Future release(Future> future) => + future.then((result) => result.asFuture); + + /// Captures the results of a stream into a stream of [Result] values. + /// + /// The returned stream will not have any error events. + /// Errors from the source stream have been converted to [ErrorResult]s. + static Stream> captureStream(Stream source) => + source.transform(new CaptureStreamTransformer()); + + /// Releases a stream of [result] values into a stream of the results. + /// + /// `Result` values of the source stream become value or error events in + /// the returned stream as appropriate. + /// Errors from the source stream become errors in the returned stream. + static Stream releaseStream(Stream> source) => + source.transform(new ReleaseStreamTransformer()); + + /// Releases results added to the returned sink as data and errors on [sink]. + /// + /// A [Result] added to the returned sink is added as a data or error event + /// on [sink]. Errors added to the returned sink are forwarded directly to + /// [sink] and so is the [EventSink.close] calls. + static EventSink> releaseSink(EventSink sink) => + new ReleaseSink(sink); + + /// Captures the events of the returned sink into results on [sink]. + /// + /// Data and error events added to the returned sink are captured into + /// [Result] values and added as data events on the provided [sink]. + /// No error events are ever added to [sink]. + /// + /// When the returned sink is closed, so is [sink]. + static EventSink captureSink(EventSink> sink) => + new CaptureSink(sink); + + /// Converts a result of a result to a single result. + /// + /// If the result is an error, or it is a `Result` value + /// which is then an error, then a result with that error is returned. + /// Otherwise both levels of results are value results, and a single + /// result with the value is returned. + static Result flatten(Result> result) { + if (result.isValue) return result.asValue.value; + return result.asError; + } + + /// Converts a sequence of results to a result of a list. + /// + /// Returns either a list of values if [results] doesn't contain any errors, + /// or the first error result in [results]. + static Result> flattenAll(Iterable> results) { + var values = []; + for (var result in results) { + if (result.isValue) { + values.add(result.asValue.value); + } else { + return result.asError; + } + } + return new Result>.value(values); + } + + /// Whether this result is a value result. + /// + /// Always the opposite of [isError]. + bool get isValue; + + /// Whether this result is an error result. + /// + /// Always the opposite of [isValue]. + bool get isError; + + /// If this is a value result, returns itself. + /// + /// Otherwise returns `null`. + ValueResult get asValue; + + /// If this is an error result, returns itself. + /// + /// Otherwise returns `null`. + ErrorResult get asError; + + /// Completes a completer with this result. + void complete(Completer completer); + + /// Adds this result to an [EventSink]. + /// + /// Calls the sink's `add` or `addError` method as appropriate. + void addTo(EventSink sink); + + /// A future that has been completed with this result as a value or an error. + Future get asFuture; +} diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart index 39fa0481..edc41fb4 100644 --- a/pkgs/async/lib/src/result/value.dart +++ b/pkgs/async/lib/src/result/value.dart @@ -4,17 +4,18 @@ import 'dart:async'; -import '../result.dart'; +import 'result.dart'; import 'error.dart'; /// A result representing a returned value. class ValueResult implements Result { + /// The result of a successful computation. final T value; bool get isValue => true; bool get isError => false; ValueResult get asValue => this; - ErrorResult get asError => null; + ErrorResult get asError => null; ValueResult(this.value); @@ -27,4 +28,9 @@ class ValueResult implements Result { } Future get asFuture => new Future.value(value); + + int get hashCode => value.hashCode ^ 0x323f1d61; + + bool operator ==(Object other) => + other is ValueResult && value == other.value; } diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index b9023ab9..1318114d 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -8,7 +8,7 @@ import 'dart:collection'; import 'package:collection/collection.dart'; import "cancelable_operation.dart"; -import "result.dart"; +import "result/result.dart"; import "subscription_stream.dart"; import "stream_completer.dart"; import "stream_splitter.dart"; @@ -859,9 +859,9 @@ class _LookAheadRequest extends _ListRequest { /// source subscription. class _CancelRequest implements _EventRequest { /// Completer for the future returned by the `cancel` call. + /// TODO(lrn); make this Completer when that is implemented. final _completer = new Completer(); - /// /// When the event is completed, it needs to cancel the active subscription /// of the `StreamQueue` object, if any. final StreamQueue _streamQueue; diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index 6cec98c7..ac4260d4 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'future_group.dart'; -import 'result.dart'; +import 'result/result.dart'; /// A class that splits a single source stream into an arbitrary number of /// (single-subscription) streams (called "branch") that emit the same events. diff --git a/pkgs/async/lib/stream_zip.dart b/pkgs/async/lib/stream_zip.dart deleted file mode 100644 index cdeb5c97..00000000 --- a/pkgs/async/lib/stream_zip.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -/// Import `async.dart` instead. -@Deprecated("Will be removed in async 2.0.0.") -library dart.pkg.async.stream_zip; - -export "src/stream_zip.dart"; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 2ee2505a..bb41d4f9 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 1.13.3 +version: 2.0.0 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart new file mode 100644 index 00000000..172e38a9 --- /dev/null +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -0,0 +1,187 @@ +// 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. + +import "dart:async"; +import "dart:math" show Random; +import "package:async/async.dart"; +import "package:test/test.dart"; + +final someStack = StackTrace.current; +Result res(int n) => new Result.value(n); +Result err(n) => new ErrorResult("$n", someStack); + +/// Helper function creating an iterable of futures. +Iterable> futures(int count, {bool throwWhen(int index)}) sync* { + for (int i = 0; i < count; i++) { + if (throwWhen != null && throwWhen(i)) { + yield new Future.error("$i", someStack); + } else { + yield new Future.value(i); + } + } +} + +main() { + test("empty", () async { + var all = await Result.captureAll(futures(0)); + expect(all, []); + }); + + group("futures only,", () { + test("single", () async { + var all = await Result.captureAll(futures(1)); + expect(all, [res(0)]); + }); + + test("multiple", () async { + var all = await Result.captureAll(futures(3)); + expect(all, [res(0), res(1), res(2)]); + }); + + test("error only", () async { + var all = + await Result.captureAll(futures(1, throwWhen: (_) => true)); + expect(all, [err(0)]); + }); + + test("multiple error only", () async { + var all = + await Result.captureAll(futures(3, throwWhen: (_) => true)); + expect(all, [err(0), err(1), err(2)]); + }); + + test("mixed error and value", () async { + var all = + await Result.captureAll(futures(4, throwWhen: (x) => x.isOdd)); + expect(all, [res(0), err(1), res(2), err(3)]); + }); + + test("completion permutation 1-2-3", () async { + var cs = new List.generate(3, (_) => new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + expect(all, completion([res(1), res(2), err(3)])); + await 0; + cs[0].complete(1); + await 0; + cs[1].complete(2); + await 0; + cs[2].completeError("3", someStack); + }); + + test("completion permutation 1-3-2", () async { + var cs = new List.generate(3, (_) => new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + expect(all, completion([res(1), res(2), err(3)])); + await 0; + cs[0].complete(1); + await 0; + cs[2].completeError("3", someStack); + await 0; + cs[1].complete(2); + }); + + test("completion permutation 2-1-3", () async { + var cs = new List.generate(3, (_) => new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + expect(all, completion([res(1), res(2), err(3)])); + await 0; + cs[1].complete(2); + await 0; + cs[0].complete(1); + await 0; + cs[2].completeError("3", someStack); + }); + + test("completion permutation 2-3-1", () async { + var cs = new List.generate(3, (_) => new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + expect(all, completion([res(1), res(2), err(3)])); + await 0; + cs[1].complete(2); + await 0; + cs[2].completeError("3", someStack); + await 0; + cs[0].complete(1); + }); + + test("completion permutation 3-1-2", () async { + var cs = new List.generate(3, (_) => new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + expect(all, completion([res(1), res(2), err(3)])); + await 0; + cs[2].completeError("3", someStack); + await 0; + cs[0].complete(1); + await 0; + cs[1].complete(2); + }); + + test("completion permutation 3-2-1", () async { + var cs = new List.generate(3, (_) => new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + expect(all, completion([res(1), res(2), err(3)])); + await 0; + cs[2].completeError("3", someStack); + await 0; + cs[1].complete(2); + await 0; + cs[0].complete(1); + }); + + var seed = new Random().nextInt(0x100000000); + int n = 25; // max 32, otherwise rnd.nextInt(1< new Completer()); + var all = Result.captureAll(cs.map((c) => c.future)); + var rnd = new Random(seed); + var throwFlags = rnd.nextInt(1 << n); // Bit-flag for throwing. + bool throws(index) => (throwFlags & (1 << index)) != 0; + var expected = new List.generate(n, (x) => throws(x) ? err(x) : res(x)); + + expect(all, completion(expected)); + + var completeFunctions = new List.generate(n, (i) { + var c = cs[i]; + return () => + throws(i) ? c.completeError("$i", someStack) : c.complete(i); + }); + completeFunctions.shuffle(rnd); + for (int i = 0; i < n; i++) { + await 0; + completeFunctions[i](); + } + }); + }); + group("values only,", () { + test("single", () async { + var all = await Result.captureAll([1]); + expect(all, [res(1)]); + }); + test("multiple", () async { + var all = await Result.captureAll([1, 2, 3]); + expect(all, [res(1), res(2), res(3)]); + }); + }); + group("mixed futures and values,", () { + test("no error", () async { + var all = await Result.captureAll(>[ + 1, + new Future(() => 2), + 3, + new Future.value(4), + ]); + expect(all, [res(1), res(2), res(3), res(4)]); + }); + test("error", () async { + var all = await Result.captureAll(>[ + 1, + new Future(() => 2), + 3, + new Future(() async => await new Future.error("4", someStack)), + new Future.value(5) + ]); + expect(all, [res(1), res(2), res(3), err(4), res(5)]); + }); + }); +} diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart new file mode 100644 index 00000000..caaf668e --- /dev/null +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -0,0 +1,54 @@ +// 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. + +import "package:async/async.dart"; +import "package:test/test.dart"; + +final someStack = StackTrace.current; +Result res(T n) => new Result.value(n); +Result err(n) => new ErrorResult("$n", someStack); + +/// Helper function creating an iterable of results. +Iterable> results(int count, {bool throwWhen(int index)}) sync* { + for (int i = 0; i < count; i++) { + if (throwWhen != null && throwWhen(i)) { + yield err(i); + } else { + yield res(i); + } + } +} + +main() { + expectAll(result, expectation) { + if (expectation.isError) { + expect(result, expectation); + } else { + expect(result.isValue, true); + expect(result.asValue.value, expectation.asValue.value); + } + } + + test("empty", () { + expectAll(Result.flattenAll(results(0)), res([])); + }); + test("single value", () { + expectAll(Result.flattenAll(results(1)), res([0])); + }); + test("single error", () { + expectAll( + Result.flattenAll(results(1, throwWhen: (_) => true)), err(0)); + }); + test("multiple values", () { + expectAll(Result.flattenAll(results(5)), res([0, 1, 2, 3, 4])); + }); + test("multiple errors", () { + expectAll(Result.flattenAll(results(5, throwWhen: (x) => x.isOdd)), + err(1)); // First error is result. + }); + test("error last", () { + expectAll( + Result.flattenAll(results(5, throwWhen: (x) => x == 4)), err(4)); + }); +} diff --git a/pkgs/async/test/result_future_test.dart b/pkgs/async/test/result/result_future_test.dart similarity index 100% rename from pkgs/async/test/result_future_test.dart rename to pkgs/async/test/result/result_future_test.dart diff --git a/pkgs/async/test/result_test.dart b/pkgs/async/test/result/result_test.dart similarity index 100% rename from pkgs/async/test/result_test.dart rename to pkgs/async/test/result/result_test.dart From 087cfe0498bd6ba3def55ffb9ef8cf05204520b9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 22 Sep 2017 12:50:12 -0700 Subject: [PATCH 095/260] Skip typed_wrapper/stream_test (dart-lang/async#38) We can't test this in 2.0 runtime semantics until test supports a 2.0-compatible platform (dart-lang/testdart-lang/async#414). Closes dart-lang/async#35 --- pkgs/async/test/typed_wrapper/stream_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/async/test/typed_wrapper/stream_test.dart b/pkgs/async/test/typed_wrapper/stream_test.dart index 88fbee65..61d3ff43 100644 --- a/pkgs/async/test/typed_wrapper/stream_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_test.dart @@ -2,6 +2,8 @@ // 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. +@Skip("Re-enable this when test can run DDC (test#414).") + import 'dart:async'; import "package:async/src/typed/stream.dart"; @@ -501,12 +503,12 @@ void main() { test("toList()", () async { var list = await wrapper.toList(); expect(() => list.first, throwsCastError); - }, skip: "Re-enable this when test can run DDC (test#414)."); + }); test("toSet()", () async { var asSet = await wrapper.toSet(); expect(() => asSet.first, throwsCastError); - }, skip: "Re-enable this when test can run DDC (test#414)."); + }); test("where()", () { expect(wrapper.where(expectAsync1((_) {}, count: 0)).first, From d2af4f1a4da59d527a66e951dc5847215fe43b38 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 3 Oct 2017 15:32:32 -0700 Subject: [PATCH 096/260] Fix a fuzzy arrow warning (dart-lang/async#41) Closes dart-lang/async#40 --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/delegate/stream_subscription.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index a1e96832..82b7877d 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.1 + +* Fix a fuzzy arrow type warning. + ## 2.0.0 * Remove deprecated public `result.dart` and `stream_zip.dart` libraries and deprecated classes `ReleaseStreamTransformer` and `CaptureStreamTransformer`. diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index e7575d82..6bef924b 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -10,7 +10,7 @@ import '../typed/stream_subscription.dart'; /// /// Subclasses can override individual methods. class DelegatingStreamSubscription implements StreamSubscription { - final StreamSubscription _source; + final StreamSubscription _source; /// Create delegating subscription forwarding calls to [sourceSubscription]. DelegatingStreamSubscription(StreamSubscription sourceSubscription) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index bb41d4f9..b5173cfe 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.0 +version: 2.0.1 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 59dd7e3687219db99af8ad17357457e898914354 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 18 Oct 2017 21:31:18 -0700 Subject: [PATCH 097/260] Rename .analysis_options to analysis_options.yaml --- pkgs/async/{.analysis_options => analysis_options.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pkgs/async/{.analysis_options => analysis_options.yaml} (100%) diff --git a/pkgs/async/.analysis_options b/pkgs/async/analysis_options.yaml similarity index 100% rename from pkgs/async/.analysis_options rename to pkgs/async/analysis_options.yaml From 41ac83c3cd311e862d5a0b296ae861dbffff61e7 Mon Sep 17 00:00:00 2001 From: keertip Date: Tue, 2 Jan 2018 10:08:05 -0800 Subject: [PATCH 098/260] Update for Dart 2.0 changes to Timer --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/restartable_timer.dart | 8 ++++++++ pkgs/async/pubspec.yaml | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 82b7877d..da57bb6c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.2 + +* Add support for Dart 2.0 library changes to class `Timer`. + ## 2.0.1 * Fix a fuzzy arrow type warning. diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart index eed51e6d..7b058212 100644 --- a/pkgs/async/lib/src/restartable_timer.dart +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -43,4 +43,12 @@ class RestartableTimer implements Timer { void cancel() { _timer.cancel(); } + + @override + // TODO: Dart 2.0 requires this method to be implemented. + // See https://github.com/dart-lang/sdk/issues/31664 + // ignore: override_on_non_overriding_getter + int get tick { + throw new UnimplementedError("tick"); + } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b5173cfe..0fe8bf2c 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.1 +version: 2.0.2 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From cda83cee91979ef83295f855add5203dbe85b6d4 Mon Sep 17 00:00:00 2001 From: keertip Date: Tue, 2 Jan 2018 10:31:56 -0800 Subject: [PATCH 099/260] Update travis to run only on dev, pub get fails on stable --- pkgs/async/.travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index 662d2feb..8d63d09f 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -2,9 +2,6 @@ language: dart sudo: false dart: - dev - - stable - - 1.23.0 - - 1.22.1 cache: directories: - $HOME/.pub-cache From 9d9c8b0ce2c94ed81c0ca6601bca5afbcfb71c97 Mon Sep 17 00:00:00 2001 From: keertip Date: Tue, 2 Jan 2018 12:15:48 -0800 Subject: [PATCH 100/260] Update SDK constraint --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0fe8bf2c..deaf2c3f 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -4,7 +4,7 @@ author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async environment: - sdk: ">=1.22.0 <2.0.0" + sdk: ">=2.0.0-dev <2.0.0" dependencies: collection: "^1.5.0" dev_dependencies: From 3b55493ed53c862897d5116185398ae5ccf11ae2 Mon Sep 17 00:00:00 2001 From: keertip Date: Tue, 2 Jan 2018 12:52:37 -0800 Subject: [PATCH 101/260] Update SDK contraint lower bound to 2.0.0-dev.15.0 --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index deaf2c3f..bfc41718 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -4,7 +4,7 @@ author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async environment: - sdk: ">=2.0.0-dev <2.0.0" + sdk: ">=2.0.0-dev.15.0 <2.0.0" dependencies: collection: "^1.5.0" dev_dependencies: From 4596b8ff25d677cf5db5b011181f9d5cc0261bcb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 5 Jan 2018 12:48:36 -0800 Subject: [PATCH 102/260] Fix a StreamQueue bug (dart-lang/async#44) Previously, StreamQueueTransaction assumed that its request was the oldest request in the queue at the time at which it was committed or rejected. This assumption wasn't always correct, and this change avoids making it. --- pkgs/async/CHANGELOG.md | 5 ++++ pkgs/async/lib/src/stream_queue.dart | 16 ++++++----- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_queue_test.dart | 39 +++++++++++++++++++++++++- 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index da57bb6c..03fff02e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.0.3 + +* Fix a bug in `StreamQueue.startTransaction()` and related methods when + rejecting a transaction that isn't the oldest request in the queue. + ## 2.0.2 * Add support for Dart 2.0 library changes to class `Timer`. diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 1318114d..d9be69d4 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -400,7 +400,7 @@ abstract class StreamQueue { // ------------------------------------------------------------------ // Methods that may be called from the request implementations to - // control the even stream. + // control the event stream. /// Matches events with requests. /// @@ -643,11 +643,13 @@ class StreamQueueTransaction { for (var queue in _queues) { queue._cancel(); } - - assert((_parent._requestQueue.first as _TransactionRequest).transaction == - this); - _parent._requestQueue.removeFirst(); - _parent._updateRequests(); + // If this is the active request in the queue, mark it as finished. + var currentRequest = _parent._requestQueue.first; + if (currentRequest is _TransactionRequest && + currentRequest.transaction == this) { + _parent._requestQueue.removeFirst(); + _parent._updateRequests(); + } } /// Throws a [StateError] if [accept] or [reject] has already been called. @@ -975,6 +977,6 @@ class _TransactionRequest implements _EventRequest { events[_eventsSent++].addTo(_controller); } if (isDone && !_controller.isClosed) _controller.close(); - return false; + return transaction._committed || _transaction._rejected; } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index bfc41718..3fd3bbb8 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.2 +version: 2.0.3 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 2e4805b8..8bd9d230 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -904,9 +904,27 @@ main() { expect(transaction.reject, throwsStateError); expect(() => transaction.commit(queue1), throwsStateError); }); + + test("before the transaction emits any events, does nothing", () async { + var controller = new StreamController(); + var events = new StreamQueue(controller.stream); + + // Queue a request before the transaction, but don't let it complete + // until we're done with the transaction. + expect(events.next, completion(equals(1))); + events.startTransaction().reject(); + expect(events.next, completion(equals(2))); + + await flushMicrotasks(); + controller.add(1); + await flushMicrotasks(); + controller.add(2); + await flushMicrotasks(); + controller.close(); + }); }); - group("when committed,", () { + group("when committed", () { test("further original requests use the committed state", () async { expect(await queue1.next, 2); await flushMicrotasks(); @@ -963,6 +981,25 @@ main() { expect(transaction.reject, throwsStateError); expect(() => transaction.commit(queue1), throwsStateError); }); + + test("before the transaction emits any events, does nothing", () async { + var controller = new StreamController(); + var events = new StreamQueue(controller.stream); + + // Queue a request before the transaction, but don't let it complete + // until we're done with the transaction. + expect(events.next, completion(equals(1))); + var transaction = events.startTransaction(); + transaction.commit(transaction.newQueue()); + expect(events.next, completion(equals(2))); + + await flushMicrotasks(); + controller.add(1); + await flushMicrotasks(); + controller.add(2); + await flushMicrotasks(); + controller.close(); + }); }); }); From c5a0e4ff0aa9bb0b9fded01ef862558f7a4e7aae Mon Sep 17 00:00:00 2001 From: Leaf Petersen Date: Fri, 9 Feb 2018 17:22:11 -0800 Subject: [PATCH 103/260] Updates for Dart 2.0 core library changes (wave 2.2) (dart-lang/async#45) --- pkgs/async/CHANGELOG.md | 10 ++++++++++ .../lib/src/result/capture_transformer.dart | 2 +- .../lib/src/result/release_transformer.dart | 2 +- .../src/single_subscription_transformer.dart | 2 +- pkgs/async/lib/src/typed/stream.dart | 20 +++++++++++-------- .../lib/src/typed_stream_transformer.dart | 2 +- pkgs/async/pubspec.yaml | 4 ++-- 7 files changed, 28 insertions(+), 14 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 03fff02e..25775030 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,13 @@ +## 2.0.4 + +* Add support for Dart 2.0 library changes to Stream and StreamTransformer. + Changed classes that implement `StreamTransformer` to extend + `StreamTransformerBase`, and changed signatures of `firstWhere`, `lastWhere`, + and `singleWhere` on classes extending `Stream`. See + also [issue 31847][sdk#31847]. + + [sdk#31847]: https://github.com/dart-lang/sdk/issues/31847 + ## 2.0.3 * Fix a bug in `StreamQueue.startTransaction()` and related methods when diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index 71e8a002..5933f0c2 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -11,7 +11,7 @@ import 'capture_sink.dart'; /// /// The result of the transformation is a stream of [Result] values and no /// error events. Exposed by [Result.captureStream]. -class CaptureStreamTransformer implements StreamTransformer> { +class CaptureStreamTransformer extends StreamTransformerBase> { const CaptureStreamTransformer(); Stream> bind(Stream source) { diff --git a/pkgs/async/lib/src/result/release_transformer.dart b/pkgs/async/lib/src/result/release_transformer.dart index fc9278d4..01865cc9 100644 --- a/pkgs/async/lib/src/result/release_transformer.dart +++ b/pkgs/async/lib/src/result/release_transformer.dart @@ -8,7 +8,7 @@ import 'result.dart'; import 'release_sink.dart'; /// A transformer that releases result events as data and error events. -class ReleaseStreamTransformer implements StreamTransformer, T> { +class ReleaseStreamTransformer extends StreamTransformerBase, T> { const ReleaseStreamTransformer(); Stream bind(Stream> source) { diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index fcd6b063..f5908605 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -13,7 +13,7 @@ import 'dart:async'; /// This also casts the source stream's events to type `T`. If the cast fails, /// the result stream will emit a [CastError]. This behavior is deprecated, and /// should not be relied upon. -class SingleSubscriptionTransformer implements StreamTransformer { +class SingleSubscriptionTransformer extends StreamTransformerBase { const SingleSubscriptionTransformer(); Stream bind(Stream stream) { diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart index a4671158..b3b0513c 100644 --- a/pkgs/async/lib/src/typed/stream.dart +++ b/pkgs/async/lib/src/typed/stream.dart @@ -53,14 +53,18 @@ class TypeSafeStream extends Stream { Stream expand(Iterable convert(T value)) => _stream.expand(_validateType(convert)); - Future firstWhere(bool test(T element), {Object defaultValue()}) => - _stream.firstWhere(_validateType(test), defaultValue: defaultValue); - - Future lastWhere(bool test(T element), {Object defaultValue()}) => - _stream.lastWhere(_validateType(test), defaultValue: defaultValue); - - Future singleWhere(bool test(T element)) async => - (await _stream.singleWhere(_validateType(test))) as T; + Future firstWhere(bool test(T element), + {Object defaultValue(), T orElse()}) => + _stream.firstWhere(_validateType(test), + defaultValue: defaultValue, orElse: orElse); + + Future lastWhere(bool test(T element), + {Object defaultValue(), T orElse()}) => + _stream.lastWhere(_validateType(test), + defaultValue: defaultValue, orElse: orElse); + + Future singleWhere(bool test(T element), {T orElse()}) async => + await _stream.singleWhere(_validateType(test), orElse: orElse); Future fold(S initialValue, S combine(S previous, T element)) => _stream.fold( diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart index 98d5e706..cb63311a 100644 --- a/pkgs/async/lib/src/typed_stream_transformer.dart +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -20,7 +20,7 @@ StreamTransformer typedStreamTransformer( /// A wrapper that coerces the type of the stream returned by an inner /// transformer. -class _TypeSafeStreamTransformer implements StreamTransformer { +class _TypeSafeStreamTransformer extends StreamTransformerBase { final StreamTransformer _inner; _TypeSafeStreamTransformer(this._inner); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3fd3bbb8..3b9bac4e 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,10 +1,10 @@ name: async -version: 2.0.3 +version: 2.0.4 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async environment: - sdk: ">=2.0.0-dev.15.0 <2.0.0" + sdk: ">=2.0.0-dev.23.0 <2.0.0" dependencies: collection: "^1.5.0" dev_dependencies: From bf7d9012d44c3752254d1490ebc63a23889a4944 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Sat, 10 Feb 2018 13:36:15 -0800 Subject: [PATCH 104/260] Stop testing Dartium (dart-lang/async#47) * only test on master branch --- pkgs/async/.travis.yml | 20 ++++++++++---------- pkgs/async/pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index 8d63d09f..2ea8538e 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -1,20 +1,20 @@ language: dart -sudo: false + dart: - dev -cache: - directories: - - $HOME/.pub-cache + dart_task: - test: --platform vm # No parallelism on Firefox (-j 1) # Causes flakiness – need to investigate - test: --platform firefox -j 1 - - test: --platform dartium - install_dartium: true - dartanalyzer - dartfmt -matrix: - exclude: - - dart: 1.22.1 - dart_task: dartfmt + +# 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/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3b9bac4e..3c9467fc 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.4 +version: 2.0.5-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 8840f5e219dc619656b31c79423a7737a40386dc Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 23 Feb 2018 17:08:03 -0800 Subject: [PATCH 105/260] Add deps for build_runner (dart-lang/async#50) Towards dart-lang/async#48 Allows us to run `pub run build_runner test -- -p chrome` to test with DDC. Exposes issues with the package and with tests so not enabling on Travis for now. --- pkgs/async/pubspec.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3c9467fc..e57d3ef7 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -11,3 +11,7 @@ dev_dependencies: fake_async: "^0.1.2" stack_trace: "^1.0.0" test: "^0.12.0" + # For building and testing with DDC + build_runner: ^0.7.11 + build_web_compilers: ^0.3.1 + build_test: ^0.10.1 From b436880c84683747d4fe9a5a63331c33906d93a8 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Mon, 26 Feb 2018 14:25:55 -0800 Subject: [PATCH 106/260] Fix runtime cast errors in StreamQueue. --- pkgs/async/CHANGELOG.md | 10 ++++++++-- pkgs/async/lib/src/stream_queue.dart | 12 ++++++------ pkgs/async/pubspec.yaml | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 25775030..0c597737 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,9 @@ +## 2.0.5 + +* Fix Dart 2.0 [runtime cast errors][sdk#27223] in StreamQueue. + +[sdk#27223]: https://github.com/dart-lang/sdk/issues/27223 + ## 2.0.4 * Add support for Dart 2.0 library changes to Stream and StreamTransformer. @@ -15,7 +21,7 @@ ## 2.0.2 -* Add support for Dart 2.0 library changes to class `Timer`. +* Add support for Dart 2.0 library changes to class `Timer`. ## 2.0.1 @@ -35,7 +41,7 @@ * Make `TypeSafeStream` extend `Stream` instead of implementing it. This ensures that new methods on `Stream` are automatically picked up, they will go through the `listen` method which type-checks every event. - + ## 1.13.2 * Fix a type-warning. diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index d9be69d4..0c81aec8 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -99,7 +99,7 @@ abstract class StreamQueue { var _eventsReceived = 0; /// Queue of events not used by a request yet. - final QueueList _eventQueue = new QueueList(); + final QueueList> _eventQueue = new QueueList(); /// Queue of pending requests. /// @@ -124,7 +124,7 @@ abstract class StreamQueue { /// one events. Future get hasNext { if (!_isClosed) { - var hasNextRequest = new _HasNextRequest(); + var hasNextRequest = new _HasNextRequest(); _addRequest(hasNextRequest); return hasNextRequest.future; } @@ -219,7 +219,7 @@ abstract class StreamQueue { Future skip(int count) { if (count < 0) throw new RangeError.range(count, 0, null, "count"); if (!_isClosed) { - var request = new _SkipRequest(count); + var request = new _SkipRequest(count); _addRequest(request); return request.future; } @@ -389,7 +389,7 @@ abstract class StreamQueue { _isClosed = true; if (!immediate) { - var request = new _CancelRequest(this); + var request = new _CancelRequest(this); _addRequest(request); return request.future; } @@ -457,7 +457,7 @@ abstract class StreamQueue { /// Called when the event source adds a new data or error event. /// Always calls [_updateRequests] after adding. - void _addResult(Result result) { + void _addResult(Result result) { _eventsReceived++; _eventQueue.add(result); _updateRequests(); @@ -485,7 +485,7 @@ abstract class StreamQueue { /// /// If the request queue is empty and the request can be completed /// immediately, it skips the queue. - void _addRequest(_EventRequest request) { + void _addRequest(_EventRequest request) { if (_requestQueue.isEmpty) { if (request.update(_eventQueue, _isDone)) return; _ensureListening(); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e57d3ef7..e163e4cb 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.5-dev +version: 2.0.5 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 6c6db71e4743f6edb63ee5403bfb2435af09c646 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Tue, 27 Feb 2018 10:03:30 -0800 Subject: [PATCH 107/260] Code font in CHANGELOG.md. --- pkgs/async/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 0c597737..c8f4d631 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,12 +1,12 @@ ## 2.0.5 -* Fix Dart 2.0 [runtime cast errors][sdk#27223] in StreamQueue. +* Fix Dart 2.0 [runtime cast errors][sdk#27223] in `StreamQueue`. [sdk#27223]: https://github.com/dart-lang/sdk/issues/27223 ## 2.0.4 -* Add support for Dart 2.0 library changes to Stream and StreamTransformer. +* Add support for Dart 2.0 library changes to `Stream` and `StreamTransformer`. Changed classes that implement `StreamTransformer` to extend `StreamTransformerBase`, and changed signatures of `firstWhere`, `lastWhere`, and `singleWhere` on classes extending `Stream`. See From 795f5d6369efd2f54be888f4bab0bf19e9e0dc06 Mon Sep 17 00:00:00 2001 From: Leaf Petersen Date: Fri, 2 Mar 2018 17:14:57 -0800 Subject: [PATCH 108/260] Forward DelegatingStream.typed to Stream.cast (dart-lang/async#54) --- pkgs/async/CHANGELOG.md | 4 + pkgs/async/lib/src/delegate/stream.dart | 5 +- pkgs/async/lib/src/typed/stream.dart | 135 ---- pkgs/async/pubspec.yaml | 2 +- .../async/test/typed_wrapper/stream_test.dart | 610 ------------------ 5 files changed, 6 insertions(+), 750 deletions(-) delete mode 100644 pkgs/async/lib/src/typed/stream.dart delete mode 100644 pkgs/async/test/typed_wrapper/stream_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index c8f4d631..08078216 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.6 + +* Add further support for Dart 2.0 library changes to `Stream`. + ## 2.0.5 * Fix Dart 2.0 [runtime cast errors][sdk#27223] in `StreamQueue`. diff --git a/pkgs/async/lib/src/delegate/stream.dart b/pkgs/async/lib/src/delegate/stream.dart index d08b4ab8..116d11f0 100644 --- a/pkgs/async/lib/src/delegate/stream.dart +++ b/pkgs/async/lib/src/delegate/stream.dart @@ -4,8 +4,6 @@ import 'dart:async'; -import '../typed/stream.dart'; - /// Simple delegating wrapper around a [Stream]. /// /// Subclasses can override individual methods, or use this to expose only the @@ -23,6 +21,5 @@ class DelegatingStream extends StreamView { /// original generic type, by asserting that its events are instances of `T` /// whenever they're provided. If they're not, the stream throws a /// [CastError]. - static Stream typed(Stream stream) => - stream is Stream ? stream : new TypeSafeStream(stream); + static Stream typed(Stream stream) => stream.cast(); } diff --git a/pkgs/async/lib/src/typed/stream.dart b/pkgs/async/lib/src/typed/stream.dart deleted file mode 100644 index b3b0513c..00000000 --- a/pkgs/async/lib/src/typed/stream.dart +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2016, 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. - -import 'dart:async'; - -import 'package:collection/collection.dart'; - -import '../utils.dart'; -import 'stream_subscription.dart'; -import '../delegate/event_sink.dart'; - -class TypeSafeStream extends Stream { - final Stream _stream; - - Future get first async => (await _stream.first) as T; - Future get last async => (await _stream.last) as T; - Future get single async => (await _stream.single) as T; - - bool get isBroadcast => _stream.isBroadcast; - Future get isEmpty => _stream.isEmpty; - Future get length => _stream.length; - - TypeSafeStream(this._stream); - - Stream asBroadcastStream( - {void onListen(StreamSubscription subscription), - void onCancel(StreamSubscription subscription)}) { - return new TypeSafeStream(_stream.asBroadcastStream( - onListen: onListen == null - ? null - : (subscription) => - onListen(new TypeSafeStreamSubscription(subscription)), - onCancel: onCancel == null - ? null - : (subscription) => - onCancel(new TypeSafeStreamSubscription(subscription)))); - } - - Stream asyncExpand(Stream convert(T event)) => - _stream.asyncExpand(_validateType(convert)); - - Stream asyncMap(convert(T event)) => - _stream.asyncMap(_validateType(convert)); - - Stream distinct([bool equals(T previous, T next)]) => - new TypeSafeStream(_stream.distinct(equals == null - ? null - : (previous, next) => equals(previous as T, next as T))); - - Future drain([E futureValue]) => _stream.drain(futureValue); - - Stream expand(Iterable convert(T value)) => - _stream.expand(_validateType(convert)); - - Future firstWhere(bool test(T element), - {Object defaultValue(), T orElse()}) => - _stream.firstWhere(_validateType(test), - defaultValue: defaultValue, orElse: orElse); - - Future lastWhere(bool test(T element), - {Object defaultValue(), T orElse()}) => - _stream.lastWhere(_validateType(test), - defaultValue: defaultValue, orElse: orElse); - - Future singleWhere(bool test(T element), {T orElse()}) async => - await _stream.singleWhere(_validateType(test), orElse: orElse); - - Future fold(S initialValue, S combine(S previous, T element)) => - _stream.fold( - initialValue, (previous, element) => combine(previous, element as T)); - - Future forEach(void action(T element)) => - _stream.forEach(_validateType(action)); - - Stream handleError(Function onError, {bool test(error)}) => - new TypeSafeStream(_stream.handleError(onError, test: test)); - - StreamSubscription listen(void onData(T value), - {Function onError, void onDone(), bool cancelOnError}) => - new TypeSafeStreamSubscription(_stream.listen(_validateType(onData), - onError: onError, onDone: onDone, cancelOnError: cancelOnError)); - - Stream map(S convert(T event)) => _stream.map(_validateType(convert)); - - // Don't forward to `_stream.pipe` because we want the consumer to see the - // type-asserted stream. - Future pipe(StreamConsumer consumer) => - consumer.addStream(this).then((_) => consumer.close()); - - Future reduce(T combine(T previous, T element)) async { - var result = await _stream - .reduce((previous, element) => combine(previous as T, element as T)); - return result as T; - } - - Stream skipWhile(bool test(T element)) => - new TypeSafeStream(_stream.skipWhile(_validateType(test))); - - Stream takeWhile(bool test(T element)) => - new TypeSafeStream(_stream.takeWhile(_validateType(test))); - - Stream timeout(Duration timeLimit, {void onTimeout(EventSink sink)}) => - new TypeSafeStream(_stream.timeout(timeLimit, - onTimeout: (sink) => onTimeout(DelegatingEventSink.typed(sink)))); - - Future> toList() async => - DelegatingList.typed(await _stream.toList()); - - Future> toSet() async => DelegatingSet.typed(await _stream.toSet()); - - // Don't forward to `_stream.transform` because we want the transformer to see - // the type-asserted stream. - Stream transform(StreamTransformer transformer) => - transformer.bind(this); - - Stream where(bool test(T element)) => - new TypeSafeStream(_stream.where(_validateType(test))); - - Future every(bool test(T element)) => - _stream.every(_validateType(test)); - - Future any(bool test(T element)) => _stream.any(_validateType(test)); - Stream skip(int count) => new TypeSafeStream(_stream.skip(count)); - Stream take(int count) => new TypeSafeStream(_stream.take(count)); - Future elementAt(int index) async => (await _stream.elementAt(index)) as T; - Future contains(Object needle) => _stream.contains(needle); - Future join([String separator = ""]) => _stream.join(separator); - String toString() => _stream.toString(); - - /// Returns a version of [function] that asserts that its argument is an - /// instance of `T`. - UnaryFunction _validateType(S function(T value)) => - function == null ? null : (value) => function(value as T); -} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e163e4cb..e8dafaa3 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.5 +version: 2.0.6 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/typed_wrapper/stream_test.dart b/pkgs/async/test/typed_wrapper/stream_test.dart deleted file mode 100644 index 61d3ff43..00000000 --- a/pkgs/async/test/typed_wrapper/stream_test.dart +++ /dev/null @@ -1,610 +0,0 @@ -// Copyright (c) 2016, 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. - -@Skip("Re-enable this when test can run DDC (test#414).") - -import 'dart:async'; - -import "package:async/src/typed/stream.dart"; -import "package:test/test.dart"; - -import '../utils.dart'; - -void main() { - group("with valid types, forwards", () { - var controller; - var wrapper; - var emptyWrapper; - var singleWrapper; - var errorWrapper; - setUp(() { - controller = new StreamController() - ..add(1) - ..add(2) - ..add(3) - ..add(4) - ..add(5) - ..close(); - - // TODO(nweiz): Use public methods when test#414 is fixed and we can run - // this on DDC. - wrapper = new TypeSafeStream(controller.stream); - emptyWrapper = new TypeSafeStream(new Stream.empty()); - singleWrapper = - new TypeSafeStream(new Stream.fromIterable([1])); - errorWrapper = new TypeSafeStream( - new Stream.fromFuture(new Future.error("oh no"))); - }); - - test("first", () { - expect(wrapper.first, completion(equals(1))); - expect(emptyWrapper.first, throwsStateError); - }); - - test("last", () { - expect(wrapper.last, completion(equals(5))); - expect(emptyWrapper.last, throwsStateError); - }); - - test("single", () { - expect(wrapper.single, throwsStateError); - expect(singleWrapper.single, completion(equals(1))); - }); - - test("isBroadcast", () { - expect(wrapper.isBroadcast, isFalse); - var broadcastWrapper = new TypeSafeStream( - new Stream.empty().asBroadcastStream()); - expect(broadcastWrapper.isBroadcast, isTrue); - }); - - test("isEmpty", () { - expect(wrapper.isEmpty, completion(isFalse)); - expect(emptyWrapper.isEmpty, completion(isTrue)); - }); - - test("length", () { - expect(wrapper.length, completion(equals(5))); - expect(emptyWrapper.length, completion(equals(0))); - }); - - group("asBroadcastStream()", () { - test("with no parameters", () { - var broadcast = wrapper.asBroadcastStream(); - expect(broadcast.toList(), completion(equals([1, 2, 3, 4, 5]))); - expect(broadcast.toList(), completion(equals([1, 2, 3, 4, 5]))); - }); - - test("with onListen", () { - var broadcast = - wrapper.asBroadcastStream(onListen: expectAsync1((subscription) { - expect(subscription, new isInstanceOf>()); - subscription.pause(); - })); - - broadcast.listen(null); - expect(controller.isPaused, isTrue); - }); - - test("with onCancel", () { - var broadcast = - wrapper.asBroadcastStream(onCancel: expectAsync1((subscription) { - expect(subscription, new isInstanceOf>()); - subscription.pause(); - })); - - broadcast.listen(null).cancel(); - expect(controller.isPaused, isTrue); - }); - }); - - test("asyncExpand()", () { - expect( - wrapper.asyncExpand((i) => new Stream.fromIterable([i, i])).toList(), - completion(equals([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]))); - }); - - test("asyncMap()", () { - expect(wrapper.asyncMap((i) => new Future.value(i * 2)).toList(), - completion(equals([2, 4, 6, 8, 10]))); - }); - - group("distinct()", () { - test("without equals", () { - expect( - wrapper.distinct().toList(), completion(equals([1, 2, 3, 4, 5]))); - - expect( - new TypeSafeStream( - new Stream.fromIterable([1, 1, 2, 2, 3, 3])) - .distinct() - .toList(), - completion(equals([1, 2, 3]))); - }); - - test("with equals", () { - expect(wrapper.distinct((i1, i2) => (i1 ~/ 2 == i2 ~/ 2)).toList(), - completion(equals([1, 2, 4]))); - }); - }); - - group("drain()", () { - test("without a value", () { - expect(wrapper.drain(), completes); - expect(() => wrapper.drain(), throwsStateError); - }); - - test("with a value", () { - expect(wrapper.drain(12), completion(equals(12))); - }); - }); - - test("expand()", () { - expect(wrapper.expand((i) => [i, i]).toList(), - completion(equals([1, 1, 2, 2, 3, 3, 4, 4, 5, 5]))); - }); - - group("firstWhere()", () { - test("finding a value", () { - expect(wrapper.firstWhere((i) => i > 3), completion(equals(4))); - }); - - test("finding no value", () { - expect(wrapper.firstWhere((i) => i > 5), throwsStateError); - }); - - test("with a default value", () { - expect(wrapper.firstWhere((i) => i > 5, defaultValue: () => "value"), - completion(equals("value"))); - }); - }); - - group("lastWhere()", () { - test("finding a value", () { - expect(wrapper.lastWhere((i) => i < 3), completion(equals(2))); - }); - - test("finding no value", () { - expect(wrapper.lastWhere((i) => i > 5), throwsStateError); - }); - - test("with a default value", () { - expect(wrapper.lastWhere((i) => i > 5, defaultValue: () => "value"), - completion(equals("value"))); - }); - }); - - group("singleWhere()", () { - test("finding a single value", () { - expect(wrapper.singleWhere((i) => i == 3), completion(equals(3))); - }); - - test("finding no value", () { - expect(wrapper.singleWhere((i) => i == 6), throwsStateError); - }); - - test("finding multiple values", () { - expect(wrapper.singleWhere((i) => i.isOdd), throwsStateError); - }); - }); - - test("fold()", () { - expect(wrapper.fold("foo", (previous, i) => previous + i.toString()), - completion(equals("foo12345"))); - }); - - test("forEach()", () async { - emptyWrapper.forEach(expectAsync1((_) {}, count: 0)); - - var results = []; - await wrapper.forEach(results.add); - expect(results, equals([1, 2, 3, 4, 5])); - }); - - group("handleError()", () { - test("without a test", () { - expect( - errorWrapper.handleError(expectAsync1((error) { - expect(error, equals("oh no")); - })).toList(), - completion(isEmpty)); - }); - - test("with a matching test", () { - expect( - errorWrapper.handleError(expectAsync1((error) { - expect(error, equals("oh no")); - }), test: expectAsync1((error) { - expect(error, equals("oh no")); - return true; - })).toList(), - completion(isEmpty)); - }); - - test("with a matching test", () { - expect( - errorWrapper.handleError(expectAsync1((_) {}, count: 0), - test: expectAsync1((error) { - expect(error, equals("oh no")); - return false; - })).toList(), - throwsA("oh no")); - }); - }); - - group("listen()", () { - test("with a callback", () { - var subscription; - subscription = wrapper.listen(expectAsync1((data) { - expect(data, equals(1)); - - subscription.onData(expectAsync1((data) { - expect(data, equals(2)); - subscription.cancel(); - })); - })); - }); - - test("with a null callback", () { - expect(wrapper.listen(null).asFuture(), completes); - }); - }); - - test("map()", () { - expect(wrapper.map((i) => i * 2).toList(), - completion(equals([2, 4, 6, 8, 10]))); - }); - - test("pipe()", () { - var consumer = new StreamController(); - expect(wrapper.pipe(consumer), completes); - expect(consumer.stream.toList(), completion(equals([1, 2, 3, 4, 5]))); - }); - - test("reduce()", () { - expect(wrapper.reduce((value, i) => value + i), completion(equals(15))); - expect(emptyWrapper.reduce((value, i) => value + i), throwsStateError); - }); - - test("skipWhile()", () { - expect(wrapper.skipWhile((i) => i < 3).toList(), - completion(equals([3, 4, 5]))); - }); - - test("takeWhile()", () { - expect( - wrapper.takeWhile((i) => i < 3).toList(), completion(equals([1, 2]))); - }); - - test("toSet()", () { - expect(wrapper.toSet(), completion(unorderedEquals([1, 2, 3, 4, 5]))); - expect( - new TypeSafeStream( - new Stream.fromIterable([1, 1, 2, 2, 3, 3])).toSet(), - completion(unorderedEquals([1, 2, 3]))); - }); - - test("transform()", () { - var transformer = new StreamTransformer.fromHandlers( - handleData: (data, sink) { - sink.add(data.toString()); - }); - - expect(wrapper.transform(transformer).toList(), - completion(equals(["1", "2", "3", "4", "5"]))); - }); - - test("where()", () { - expect(wrapper.where((i) => i.isOdd).toList(), - completion(equals([1, 3, 5]))); - }); - - group("any()", () { - test("with matches", () { - expect(wrapper.any((i) => i > 3), completion(isTrue)); - }); - - test("without matches", () { - expect(wrapper.any((i) => i > 5), completion(isFalse)); - }); - }); - - group("every()", () { - test("with all matches", () { - expect(wrapper.every((i) => i < 6), completion(isTrue)); - }); - - test("with some non-matches", () { - expect(wrapper.every((i) => i > 3), completion(isFalse)); - }); - }); - - group("skip()", () { - test("with a valid index", () { - expect(wrapper.skip(3).toList(), completion(equals([4, 5]))); - }); - - test("with a longer index than length", () { - expect(wrapper.skip(6).toList(), completion(isEmpty)); - }); - - test("with a negative index", () { - expect(() => wrapper.skip(-1), throwsArgumentError); - }); - }); - - group("take()", () { - test("with a valid index", () { - expect(wrapper.take(3).toList(), completion(equals([1, 2, 3]))); - }); - - test("with a longer index than length", () { - expect(wrapper.take(6).toList(), completion(equals([1, 2, 3, 4, 5]))); - }); - - test("with a negative index", () { - expect(wrapper.take(-1).toList(), completion(isEmpty)); - }); - }); - - group("elementAt()", () { - test("with a valid index", () { - expect(wrapper.elementAt(3), completion(equals(4))); - }); - - test("with too high an index", () { - expect(wrapper.elementAt(6), throwsRangeError); - }); - - test("with a negative index", () { - expect(wrapper.elementAt(-1), throwsArgumentError); - }); - }); - - group("contains()", () { - test("with an element", () { - expect(wrapper.contains(2), completion(isTrue)); - }); - - test("with a non-element", () { - expect(wrapper.contains(6), completion(isFalse)); - }); - - test("with a non-element of a different type", () { - expect(wrapper.contains("foo"), completion(isFalse)); - }); - }); - - group("join()", () { - test("without a separator", () { - expect(wrapper.join(), completion(equals("12345"))); - }); - - test("with a separator", () { - expect(wrapper.join(" "), completion(equals("1 2 3 4 5"))); - }); - }); - - test("toString()", () { - expect(wrapper.toString(), contains("Stream")); - }); - }); - - group("with invalid types", () { - var wrapper; - var singleWrapper; - setUp(() { - wrapper = new TypeSafeStream( - new Stream.fromIterable(["foo", "bar", "baz"])); - singleWrapper = - new TypeSafeStream(new Stream.fromIterable(["foo"])); - }); - - group("throws a CastError for", () { - test("first", () { - expect(wrapper.first, throwsCastError); - }); - - test("last", () { - expect(wrapper.last, throwsCastError); - }); - - test("single", () { - expect(singleWrapper.single, throwsCastError); - }); - - test("asBroadcastStream()", () { - var broadcast = wrapper.asBroadcastStream(); - expect(broadcast.first, throwsCastError); - }); - - test("asyncExpand()", () { - expect(wrapper.asyncExpand(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - test("asyncMap()", () { - expect(wrapper.asyncMap(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - group("distinct()", () { - test("without equals", () { - expect(wrapper.distinct().first, throwsCastError); - }); - - test("with equals", () { - expect(wrapper.distinct(expectAsync2((_, __) {}, count: 0)).first, - throwsCastError); - }); - }); - - test("expand()", () { - expect(wrapper.expand(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - test("firstWhere()", () { - expect(wrapper.firstWhere(expectAsync1((_) {}, count: 0)), - throwsCastError); - }); - - test("lastWhere()", () { - expect( - wrapper.lastWhere(expectAsync1((_) {}, count: 0)), throwsCastError); - }); - - test("singleWhere()", () { - expect(wrapper.singleWhere(expectAsync1((_) {}, count: 0)), - throwsCastError); - }); - - test("fold()", () { - expect(wrapper.fold("foo", expectAsync2((_, __) {}, count: 0)), - throwsCastError); - }); - - test("forEach()", () async { - expect( - wrapper.forEach(expectAsync1((_) {}, count: 0)), throwsCastError); - }); - - test("handleError()", () { - expect(wrapper.handleError(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - test("listen()", () { - expect(() => wrapper.take(1).listen(expectAsync1((_) {}, count: 0)), - throwsZonedCastError); - }); - - test("map()", () { - expect( - wrapper.map(expectAsync1((_) {}, count: 0)).first, throwsCastError); - }); - - test("reduce()", () { - expect(wrapper.reduce(expectAsync2((_, __) {}, count: 0)), - throwsCastError); - }); - - test("skipWhile()", () { - expect(wrapper.skipWhile(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - test("takeWhile()", () { - expect(wrapper.takeWhile(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - test("toList()", () async { - var list = await wrapper.toList(); - expect(() => list.first, throwsCastError); - }); - - test("toSet()", () async { - var asSet = await wrapper.toSet(); - expect(() => asSet.first, throwsCastError); - }); - - test("where()", () { - expect(wrapper.where(expectAsync1((_) {}, count: 0)).first, - throwsCastError); - }); - - test("any()", () { - expect(wrapper.any(expectAsync1((_) {}, count: 0)), throwsCastError); - }); - - test("every()", () { - expect(wrapper.every(expectAsync1((_) {}, count: 0)), throwsCastError); - }); - - test("skip()", () { - expect(wrapper.skip(1).first, throwsCastError); - }); - - test("take()", () { - expect(wrapper.take(1).first, throwsCastError); - }); - - test("elementAt()", () { - expect(wrapper.elementAt(1), throwsCastError); - }); - }); - - group("doesn't throw a CastError for", () { - test("single", () { - expect(wrapper.single, throwsStateError); - }); - - test("length", () { - expect(wrapper.length, completion(equals(3))); - }); - - test("isBroadcast", () { - expect(wrapper.isBroadcast, isFalse); - }); - - test("isEmpty", () { - expect(wrapper.isEmpty, completion(isFalse)); - }); - - group("drain()", () { - test("without a value", () { - expect(wrapper.drain(), completes); - expect(() => wrapper.drain(), throwsStateError); - }); - - test("with a value", () { - expect(wrapper.drain(12), completion(equals(12))); - }); - }); - - test("skip()", () { - expect(() => wrapper.skip(-1), throwsArgumentError); - }); - - group("elementAt()", () { - test("with too high an index", () { - expect(wrapper.elementAt(6), throwsRangeError); - }); - - test("with a negative index", () { - expect(wrapper.elementAt(-1), throwsArgumentError); - }); - }); - - group("contains()", () { - test("with an element", () { - expect(wrapper.contains("foo"), completion(isTrue)); - }); - - test("with a non-element", () { - expect(wrapper.contains("qux"), completion(isFalse)); - }); - - test("with a non-element of a different type", () { - expect(wrapper.contains(1), completion(isFalse)); - }); - }); - - group("join()", () { - test("without a separator", () { - expect(wrapper.join(), completion(equals("foobarbaz"))); - }); - - test("with a separator", () { - expect(wrapper.join(" "), completion(equals("foo bar baz"))); - }); - }); - - test("toString()", () { - expect(wrapper.toString(), contains("Stream")); - }); - }); - }); -} From 22e4c3aa14e2409582ad0703d86552a8515f18ab Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 1 May 2018 17:05:14 -0700 Subject: [PATCH 109/260] Declare support for the latest fake_async (dart-lang/async#59) Closes dart-lang/async#49 --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e8dafaa3..3183b45d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: collection: "^1.5.0" dev_dependencies: - fake_async: "^0.1.2" + fake_async: ">=0.1.2 <2.0.0" stack_trace: "^1.0.0" test: "^0.12.0" # For building and testing with DDC From 1643a97f5c6166b6c67620cb6645a3530579dc22 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 2 May 2018 12:55:05 -0700 Subject: [PATCH 110/260] Fix Dart 2 runtime errors (dart-lang/async#58) Closes dart-lang/async#57 --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/result/capture_sink.dart | 2 +- pkgs/async/lib/src/result/capture_transformer.dart | 9 +++------ pkgs/async/lib/src/stream_splitter.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/result/result_test.dart | 9 ++++----- pkgs/async/test/stream_completer_test.dart | 4 ++-- pkgs/async/test/stream_group_test.dart | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 08078216..dc79f29b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.7 + +* Fix Dart 2 runtime errors. + ## 2.0.6 * Add further support for Dart 2.0 library changes to `Stream`. diff --git a/pkgs/async/lib/src/result/capture_sink.dart b/pkgs/async/lib/src/result/capture_sink.dart index c85b5532..742c1601 100644 --- a/pkgs/async/lib/src/result/capture_sink.dart +++ b/pkgs/async/lib/src/result/capture_sink.dart @@ -13,7 +13,7 @@ class CaptureSink implements EventSink { CaptureSink(EventSink> sink) : _sink = sink; void add(T value) { - _sink.add(new Result.value(value)); + _sink.add(new Result.value(value)); } void addError(Object error, [StackTrace stackTrace]) { diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index 5933f0c2..b9a538ab 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -14,10 +14,7 @@ import 'capture_sink.dart'; class CaptureStreamTransformer extends StreamTransformerBase> { const CaptureStreamTransformer(); - Stream> bind(Stream source) { - return new Stream>.eventTransformed(source, _createSink); - } - - // Since Stream.eventTransformed is not generic, this method can be static. - static EventSink _createSink(EventSink sink) => new CaptureSink(sink); + Stream> bind(Stream source) => + new Stream>.eventTransformed( + source, (sink) => new CaptureSink(sink)); } diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index ac4260d4..3616501c 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -60,7 +60,7 @@ class StreamSplitter { static List> splitFrom(Stream stream, [int count]) { if (count == null) count = 2; var splitter = new StreamSplitter(stream); - var streams = new List.generate(count, (_) => splitter.split()); + var streams = new List>.generate(count, (_) => splitter.split()); splitter.close(); return streams; } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3183b45d..ae09136e 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.6 +version: 2.0.7-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index 210ae3fc..23c9f630 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -173,8 +173,8 @@ void main() { }); test("capture stream", () { - StreamController c = new StreamController(); - Stream stream = Result.captureStream(c.stream); + var c = new StreamController(); + var stream = Result.captureStream(c.stream); var expectedList = new Queue.from([ new Result.value(42), new Result.error("BAD", stack), @@ -185,9 +185,8 @@ void main() { expectResult(actual, expectedList.removeFirst()); } - stream.listen(expectAsync1(listener, count: 3), onError: (e, s) { - fail("Unexpected error: $e"); - }, onDone: expectAsync0(() {}), cancelOnError: true); + stream.listen(expectAsync1(listener, count: 3), + onDone: expectAsync0(() {}), cancelOnError: true); c.add(42); c.addError("BAD", stack); c.add(37); diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 2e9ff9be..4f30a7a0 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -140,7 +140,7 @@ main() { test("cancelOnError true when listening before linking stream", () async { var completer = new StreamCompleter(); - var lastEvent = -1; + Object lastEvent = -1; var controller = new StreamController(); completer.stream.listen((value) { expect(value, lessThan(3)); @@ -173,7 +173,7 @@ main() { test("cancelOnError true when listening after linking stream", () async { var completer = new StreamCompleter(); - var lastEvent = -1; + Object lastEvent = -1; var controller = new StreamController(); completer.setSourceStream(controller.stream); controller.add(1); diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 65ddb422..95eb8c1a 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -49,7 +49,7 @@ main() { expect(streamGroup.close(), completes); var transformed = streamGroup.stream.transform( - new StreamTransformer.fromHandlers( + new StreamTransformer.fromHandlers( handleError: (error, _, sink) => sink.add("error: $error"))); expect(transformed.toList(), completion(equals(["error: first", "error: second"]))); @@ -72,7 +72,7 @@ main() { expect(streamGroup.close(), completes); var transformed = streamGroup.stream.transform( - new StreamTransformer.fromHandlers( + new StreamTransformer.fromHandlers( handleData: (data, sink) => sink.add("data: $data"), handleError: (error, _, sink) => sink.add("error: $error"))); expect( From 412ee717af77f8af0447d01362ee884879ebe755 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Thu, 3 May 2018 14:04:11 +0200 Subject: [PATCH 111/260] Remove upper case constants (dart-lang/async#56) Remove usage of upper-case constants. --- pkgs/async/CHANGELOG.md | 1 + pkgs/async/lib/src/async_cache.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_group_test.dart | 2 +- pkgs/async/test/stream_splitter_test.dart | 2 +- pkgs/async/test/typed_wrapper/future_test.dart | 6 +++--- pkgs/async/test/utils.dart | 2 +- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index dc79f29b..08c87347 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.0.7 * Fix Dart 2 runtime errors. +* Stop using deprecated constants from the SDK. ## 2.0.6 diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index af52cd06..8c252675 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -42,7 +42,7 @@ class AsyncCache { /// An ephemeral cache guarantees that a callback function will only be /// executed at most once concurrently. This is useful for requests for which /// data is updated frequently but stale data is acceptable. - factory AsyncCache.ephemeral() => new AsyncCache(Duration.ZERO); + factory AsyncCache.ephemeral() => new AsyncCache(Duration.zero); /// Creates a cache that invalidates its contents after [duration] has passed. /// diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index ae09136e..e1ca8019 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.7-dev +version: 2.0.7 author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 95eb8c1a..d94f4d0c 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -721,4 +721,4 @@ void regardlessOfType(StreamGroup newStreamGroup()) { } /// Wait for all microtasks to complete. -Future flushMicrotasks() => new Future.delayed(Duration.ZERO); +Future flushMicrotasks() => new Future.delayed(Duration.zero); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 68a6b74c..70266ee2 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -289,4 +289,4 @@ main() { } /// Wait for all microtasks to complete. -Future flushMicrotasks() => new Future.delayed(Duration.ZERO); +Future flushMicrotasks() => new Future.delayed(Duration.zero); diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart index 0c8b00ac..7fb91c82 100644 --- a/pkgs/async/test/typed_wrapper/future_test.dart +++ b/pkgs/async/test/typed_wrapper/future_test.dart @@ -61,12 +61,12 @@ void main() { expect( new TypeSafeFuture(new Completer().future) - .timeout(Duration.ZERO), + .timeout(Duration.zero), throwsA(new isInstanceOf())); expect( new TypeSafeFuture(new Completer().future) - .timeout(Duration.ZERO, onTimeout: expectAsync0(() => 15)), + .timeout(Duration.zero, onTimeout: expectAsync0(() => 15)), completion(equals(15))); }); }); @@ -100,7 +100,7 @@ void main() { expect( new TypeSafeFuture(new Completer().future) - .timeout(Duration.ZERO, onTimeout: expectAsync0(() => "foo")) + .timeout(Duration.zero, onTimeout: expectAsync0(() => "foo")) .then((_) {}), throwsCastError); }); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 92708868..39df0f6b 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -9,7 +9,7 @@ import "package:async/async.dart"; import "package:test/test.dart"; /// A zero-millisecond timer should wait until after all microtasks. -Future flushMicrotasks() => new Future.delayed(Duration.ZERO); +Future flushMicrotasks() => new Future.delayed(Duration.zero); typedef void OptionalArgAction([a, b]); From 0aec6aa4c5f66ee5ea7c30ec0d40133e8bd88216 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 22 Jun 2018 12:50:39 -0700 Subject: [PATCH 112/260] Enable prefer_typing_uninitialized_variables (dart-lang/async#63) This helps rule out some possibilities for the new Dart 2 failure and is good practice. Also: - Update dependencies on build and test packages. - Ignore todo from the analysis server. - Move off of deprecated APIs from test. --- pkgs/async/analysis_options.yaml | 5 +++++ pkgs/async/lib/src/result/error.dart | 2 +- pkgs/async/lib/src/result/result.dart | 2 +- .../src/single_subscription_transformer.dart | 2 +- pkgs/async/pubspec.yaml | 14 +++++++------- pkgs/async/test/async_memoizer_test.dart | 2 +- pkgs/async/test/cancelable_operation_test.dart | 4 ++-- pkgs/async/test/future_group_test.dart | 2 +- pkgs/async/test/lazy_stream_test.dart | 2 +- pkgs/async/test/result/result_future_test.dart | 4 ++-- pkgs/async/test/result/result_test.dart | 17 +++++++++++------ pkgs/async/test/stream_completer_test.dart | 2 +- pkgs/async/test/stream_group_test.dart | 6 +++--- pkgs/async/test/stream_queue_test.dart | 8 ++++---- pkgs/async/test/stream_sink_completer_test.dart | 2 +- .../test/stream_sink_transformer_test.dart | 2 +- pkgs/async/test/stream_splitter_test.dart | 2 +- pkgs/async/test/stream_zip_test.dart | 2 +- pkgs/async/test/subscription_stream_test.dart | 10 +++++----- pkgs/async/test/typed_wrapper/future_test.dart | 4 ++-- .../typed_wrapper/stream_subscription_test.dart | 12 ++++++------ pkgs/async/test/utils.dart | 4 ++-- 22 files changed, 60 insertions(+), 50 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index a10d4c5a..80d23681 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,2 +1,7 @@ analyzer: strong-mode: true + errors: + todo: ignore +linter: + rules: + - prefer_typing_uninitialized_variables diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index 45e06a20..2feb2c25 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -10,7 +10,7 @@ import 'value.dart'; /// A result representing a thrown error. class ErrorResult implements Result { /// The error object that was thrown. - final error; + final Object error; /// The stack trace corresponding to where [error] was thrown. final StackTrace stackTrace; diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 98b5aa43..25503b57 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -98,7 +98,7 @@ abstract class Result { static Future>> captureAll(Iterable> elements) { var results = >[]; int pending = 0; - var completer; + Completer>> completer; for (var element in elements) { if (element is Future) { int i = results.length; diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index f5908605..ff171899 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -17,7 +17,7 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { const SingleSubscriptionTransformer(); Stream bind(Stream stream) { - var subscription; + StreamSubscription subscription; var controller = new StreamController( sync: true, onCancel: () => subscription.cancel()); subscription = stream.listen((value) { diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e1ca8019..36246c28 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,17 +1,17 @@ name: async -version: 2.0.7 +version: 2.0.8-dev author: Dart Team description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async environment: sdk: ">=2.0.0-dev.23.0 <2.0.0" dependencies: - collection: "^1.5.0" + collection: ^1.5.0 dev_dependencies: - fake_async: ">=0.1.2 <2.0.0" - stack_trace: "^1.0.0" - test: "^0.12.0" + fake_async: ^1.0.0 + stack_trace: ^1.0.0 + test: ^1.0.0 # For building and testing with DDC - build_runner: ^0.7.11 - build_web_compilers: ^0.3.1 + build_runner: ^0.8.0 + build_web_compilers: ^0.4.0 build_test: ^0.10.1 diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart index dc008f1e..380f58dc 100644 --- a/pkgs/async/test/async_memoizer_test.dart +++ b/pkgs/async/test/async_memoizer_test.dart @@ -6,7 +6,7 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; main() { - var cache; + AsyncMemoizer cache; setUp(() => cache = new AsyncMemoizer()); test("runs the function only the first time runOnce() is called", () async { diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index bded4025..8c92e2a2 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -11,7 +11,7 @@ import 'utils.dart'; void main() { group("without being canceled", () { - var completer; + CancelableCompleter completer; setUp(() { completer = new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); @@ -118,7 +118,7 @@ void main() { test("fires onCancel", () { var canceled = false; - var completer; + CancelableCompleter completer; completer = new CancelableCompleter(onCancel: expectAsync0(() { expect(completer.isCanceled, isTrue); canceled = true; diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index af04799b..b6f75ba3 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - var futureGroup; + FutureGroup futureGroup; setUp(() { futureGroup = new FutureGroup(); }); diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index 8da13665..0c2e3441 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -96,7 +96,7 @@ main() { }); test("a lazy stream can't be listened to from within its callback", () { - var stream; + LazyStream stream; stream = new LazyStream(expectAsync0(() { expect(() => stream.listen(null), throwsStateError); return new Stream.empty(); diff --git a/pkgs/async/test/result/result_future_test.dart b/pkgs/async/test/result/result_future_test.dart index d8b4e01e..ffdbb7cf 100644 --- a/pkgs/async/test/result/result_future_test.dart +++ b/pkgs/async/test/result/result_future_test.dart @@ -9,8 +9,8 @@ import 'package:stack_trace/stack_trace.dart'; import 'package:test/test.dart'; void main() { - var completer; - var future; + Completer completer; + ResultFuture future; setUp(() { completer = new Completer(); future = new ResultFuture(completer.future); diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index 23c9f630..33e146e6 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -301,12 +301,17 @@ void main() { test("handle neither unary nor binary", () { ErrorResult result = new Result.error("error", stack); - expect(() => result.handle(() => fail("unreachable")), throws); - expect(() => result.handle((a, b, c) => fail("unreachable")), throws); - expect(() => result.handle((a, b, {c}) => fail("unreachable")), throws); - expect(() => result.handle((a, {b}) => fail("unreachable")), throws); - expect(() => result.handle(({a, b}) => fail("unreachable")), throws); - expect(() => result.handle(({a}) => fail("unreachable")), throws); + expect(() => result.handle(() => fail("unreachable")), throwsA(anything)); + expect(() => result.handle((a, b, c) => fail("unreachable")), + throwsA(anything)); + expect(() => result.handle((a, b, {c}) => fail("unreachable")), + throwsA(anything)); + expect(() => result.handle((a, {b}) => fail("unreachable")), + throwsA(anything)); + expect(() => result.handle(({a, b}) => fail("unreachable")), + throwsA(anything)); + expect( + () => result.handle(({a}) => fail("unreachable")), throwsA(anything)); }); } diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 4f30a7a0..55b4ce34 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -77,7 +77,7 @@ main() { var completer = new StreamCompleter(); var lastEvent = -1; var controller = new StreamController(); - var subscription; + StreamSubscription subscription; subscription = completer.stream.listen((value) { expect(value, lessThan(3)); lastEvent = value; diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index d94f4d0c..83b685c3 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; main() { group("single-subscription", () { - var streamGroup; + StreamGroup streamGroup; setUp(() { streamGroup = new StreamGroup(); }); @@ -250,7 +250,7 @@ main() { }); group("broadcast", () { - var streamGroup; + StreamGroup streamGroup; setUp(() { streamGroup = new StreamGroup.broadcast(); }); @@ -439,7 +439,7 @@ main() { } void regardlessOfType(StreamGroup newStreamGroup()) { - var streamGroup; + StreamGroup streamGroup; setUp(() { streamGroup = newStreamGroup(); }); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 8bd9d230..7a3d718f 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -406,7 +406,7 @@ main() { expect(controller.hasListener, isTrue); expect(controller.isPaused, isFalse); - var lastEvent; + dynamic lastEvent; subscription.onData((value) => lastEvent = value); controller.add(2); @@ -445,7 +445,7 @@ main() { expect(await events.peek, 4); expect(await events.next, 4); // Throws at end. - expect(events.peek, throws); + expect(events.peek, throwsA(anything)); await events.cancel(); }); test("multiple requests at the same time", () async { @@ -610,7 +610,7 @@ main() { var controller = new StreamController(); var events = new StreamQueue(controller.stream); - var hasNext; + bool hasNext; events.hasNext.then((result) { hasNext = result; }); @@ -626,7 +626,7 @@ main() { var controller = new StreamController(); var events = new StreamQueue(controller.stream); - var hasNext; + bool hasNext; events.hasNext.then((result) { hasNext = result; }); diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 3c8b576e..a149a7f9 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -10,7 +10,7 @@ import "package:test/test.dart"; import "utils.dart"; main() { - var completer; + StreamSinkCompleter completer; setUp(() { completer = new StreamSinkCompleter(); }); diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index 208a03a7..552bdaa3 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -10,7 +10,7 @@ import "package:test/test.dart"; import "utils.dart"; void main() { - var controller; + StreamController controller; setUp(() { controller = new StreamController(); }); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 70266ee2..c118b125 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; main() { StreamController controller; - var splitter; + StreamSplitter splitter; setUp(() { controller = new StreamController(); splitter = new StreamSplitter(controller.stream); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 71d8eeef..018c8ab7 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -307,7 +307,7 @@ main() { var s2 = new Stream.fromIterable([1, 3, 5, 7]); var sz = new StreamZip([s1, s2]); int ctr = 0; - var sub; + StreamSubscription sub; sub = sz.listen(expectAsync1((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); if (ctr == 1) { diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 6e2c9d5b..804e92f1 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -40,7 +40,7 @@ main() { var sourceSubscription = stream.listen(null); var subscriptionStream = new SubscriptionStream(sourceSubscription); var subscription = subscriptionStream.listen(null); - expect(() => subscriptionStream.listen(null), throws); + expect(() => subscriptionStream.listen(null), throwsA(anything)); await subscription.cancel(); }); @@ -49,7 +49,7 @@ main() { var sourceSubscription = controller.stream.listen(null); var subscriptionStream = new SubscriptionStream(sourceSubscription); expect(controller.isPaused, isTrue); - var lastEvent; + dynamic lastEvent; var subscription = subscriptionStream.listen((value) { lastEvent = value; }); @@ -72,8 +72,8 @@ main() { group("cancelOnError source:", () { for (var sourceCancels in [false, true]) { group("${sourceCancels ? "yes" : "no"}:", () { - var subscriptionStream; - var onCancel; // Completes if source stream is canceled before done. + SubscriptionStream subscriptionStream; + Future onCancel; // Completes if source stream is canceled before done. setUp(() { var cancelCompleter = new Completer(); var source = createErrorStream(cancelCompleter); @@ -142,7 +142,7 @@ main() { var subscription = subscriptionStream.listen(null, cancelOnError: cancelOnError); - expect(subscription.asFuture(), throws); + expect(subscription.asFuture(), throwsA(anything)); }); }); } diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart index 7fb91c82..ea3303f8 100644 --- a/pkgs/async/test/typed_wrapper/future_test.dart +++ b/pkgs/async/test/typed_wrapper/future_test.dart @@ -11,7 +11,7 @@ import '../utils.dart'; void main() { group("with valid types, forwards", () { - var wrapper; + TypeSafeFuture wrapper; TypeSafeFuture errorWrapper; setUp(() { wrapper = new TypeSafeFuture(new Future.value(12)); @@ -62,7 +62,7 @@ void main() { expect( new TypeSafeFuture(new Completer().future) .timeout(Duration.zero), - throwsA(new isInstanceOf())); + throwsA(new TypeMatcher())); expect( new TypeSafeFuture(new Completer().future) diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index f52abe7e..50c07b67 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -11,9 +11,9 @@ import '../utils.dart'; void main() { group("with valid types, forwards", () { - var controller; - var wrapper; - var isCanceled; + StreamController controller; + StreamSubscription wrapper; + bool isCanceled; setUp(() { controller = new StreamController(onCancel: () { isCanceled = true; @@ -68,9 +68,9 @@ void main() { }); group("with invalid types,", () { - var controller; - var wrapper; - var isCanceled; + StreamController controller; + StreamSubscription wrapper; + bool isCanceled; setUp(() { controller = new StreamController(onCancel: () { isCanceled = true; diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 39df0f6b..3f8c0ef9 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -38,10 +38,10 @@ Matcher throwsZoned(matcher) => predicate((callback) { /// A matcher that runs a callback in its own zone and asserts that that zone /// emits a [CastError]. -final throwsZonedCastError = throwsZoned(new isInstanceOf()); +final throwsZonedCastError = throwsZoned(new TypeMatcher()); /// A matcher that matches a callback or future that throws a [CastError]. -final throwsCastError = throwsA(new isInstanceOf()); +final throwsCastError = throwsA(new TypeMatcher()); /// A badly behaved stream which throws if it's ever listened to. /// From 6f5152b2c01ecd18210a4f2207916e7e7e47f13e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 27 Jun 2018 15:45:52 -0700 Subject: [PATCH 113/260] misc: update build_runner dep and update one test file for dartfmt (dart-lang/async#64) --- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_queue_test.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 36246c28..02800df0 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -12,6 +12,6 @@ dev_dependencies: stack_trace: ^1.0.0 test: ^1.0.0 # For building and testing with DDC - build_runner: ^0.8.0 + build_runner: ^0.9.0 build_web_compilers: ^0.4.0 build_test: ^0.10.1 diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 7a3d718f..32cc5050 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -153,8 +153,8 @@ main() { test("multiple requests at the same time", () async { var events = new StreamQueue(createStream()); - var result = await Future - .wait([events.next, events.next, events.next, events.next]); + var result = await Future.wait( + [events.next, events.next, events.next, events.next]); expect(result, [1, 2, 3, 4]); await events.cancel(); }); From b6dd182ab5fb67721f8aba0c29e16365859d4dd1 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 29 Jun 2018 12:42:54 -0700 Subject: [PATCH 114/260] Fix places awaiting void, for dart 2. Looks like cache.invalidate does do some asynchronous stuff (`splitter.close()`, but notably not `_stale.close()` which returns void). Return the future for that stuff without changing its order of operations. Also some place awaiting `expect()` which appeared to be intended to be awaiting `cancel()`, switching those still passes tests. --- pkgs/async/lib/src/async_cache.dart | 5 +++-- pkgs/async/test/async_cache_test.dart | 2 +- pkgs/async/test/stream_queue_test.dart | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 8c252675..c2d7c011 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -89,12 +89,13 @@ class AsyncCache { } /// Removes any cached value. - void invalidate() { + Future invalidate() { _cachedValueFuture = null; - _cachedStreamSplitter?.close(); + Future invalidate = _cachedStreamSplitter?.close(); _cachedStreamSplitter = null; _stale?.cancel(); _stale = null; + return invalidate; } void _startStaleTimer() { diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 747835b6..0a5e0f06 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -32,7 +32,7 @@ void main() { var completer = new Completer(); expect(cache.fetch(() => completer.future), completion('Expensive')); expect(cache.fetch(expectAsync0(() {}, count: 0)), completion('Expensive')); - await completer.complete('Expensive'); + completer.complete('Expensive'); }); test('should fetch via a callback again when cache expires', () { diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 32cc5050..37dedb94 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -517,8 +517,8 @@ main() { expect(await events.next, 1); expect(controller.hasListener, isTrue); - events.cancel(immediate: true); - await expect(controller.hasListener, isFalse); + await events.cancel(immediate: true); + expect(controller.hasListener, isFalse); }); test("cancels the underlying subscription when called before any event", From 9cb783fd0a9836908b119eeaab89036d635da9bc Mon Sep 17 00:00:00 2001 From: BC Ko Date: Mon, 9 Jul 2018 14:33:02 -0700 Subject: [PATCH 115/260] Update .gitignore to new `dart_tool` pub cache (dart-lang/async#60) * Update .gitignore to new `dart_tool` pub cache dart-lang/sdkdart-lang/async#32030 * delete .pub and packages https://www.dartlang.org/guides/libraries/private-files --- pkgs/async/.gitignore | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.gitignore b/pkgs/async/.gitignore index 7a2e8dd9..e50bf967 100644 --- a/pkgs/async/.gitignore +++ b/pkgs/async/.gitignore @@ -1,9 +1,11 @@ +# See https://www.dartlang.org/guides/libraries/private-files + .buildlog .DS_Store .idea -.pub/ + +.dart_tool/ .settings/ build/ -packages pubspec.lock .packages From 3273b15362240484e5d1956d2868da2601cd7c4f Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Mon, 9 Jul 2018 14:37:01 -0700 Subject: [PATCH 116/260] Delete typed future, use Dart 2 instead. (dart-lang/async#55) --- pkgs/async/CHANGELOG.md | 4 + pkgs/async/lib/src/delegate/future.dart | 5 +- pkgs/async/lib/src/typed/future.dart | 25 ---- .../async/test/typed_wrapper/future_test.dart | 109 ------------------ 4 files changed, 6 insertions(+), 137 deletions(-) delete mode 100644 pkgs/async/lib/src/typed/future.dart delete mode 100644 pkgs/async/test/typed_wrapper/future_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 08c87347..009aed7c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.8 + +* Deprecate `DelegatingFuture.typed`, it is not necessary in Dart 2. + ## 2.0.7 * Fix Dart 2 runtime errors. diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 129ad49d..62ff96f2 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -4,8 +4,6 @@ import 'dart:async'; -import '../typed/future.dart'; - /// A wrapper that forwards calls to a [Future]. class DelegatingFuture implements Future { /// The wrapped [Future]. @@ -19,8 +17,9 @@ class DelegatingFuture implements Future { /// This soundly converts a [Future] to a `Future`, regardless of its /// original generic type, by asserting that its value is an instance of `T` /// whenever it's provided. If it's not, the future throws a [CastError]. + @Deprecated('Use future.then((v) => v as T) instead.') static Future typed(Future future) => - future is Future ? future : new TypeSafeFuture(future); + future is Future ? future : future.then((v) => v as T); Stream asStream() => _future.asStream(); diff --git a/pkgs/async/lib/src/typed/future.dart b/pkgs/async/lib/src/typed/future.dart deleted file mode 100644 index 4630af7d..00000000 --- a/pkgs/async/lib/src/typed/future.dart +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2016, 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. - -import 'dart:async'; - -class TypeSafeFuture implements Future { - final Future _future; - - TypeSafeFuture(this._future); - - Stream asStream() => _future.then((value) => value as T).asStream(); - - Future catchError(Function onError, {bool test(Object error)}) async => - new TypeSafeFuture(_future.catchError(onError, test: test)); - - Future then(dynamic onValue(T value), {Function onError}) => - _future.then((value) => onValue(value as T), onError: onError); - - Future whenComplete(action()) => - new TypeSafeFuture(_future.whenComplete(action)); - - Future timeout(Duration timeLimit, {onTimeout()}) => - new TypeSafeFuture(_future.timeout(timeLimit, onTimeout: onTimeout)); -} diff --git a/pkgs/async/test/typed_wrapper/future_test.dart b/pkgs/async/test/typed_wrapper/future_test.dart deleted file mode 100644 index ea3303f8..00000000 --- a/pkgs/async/test/typed_wrapper/future_test.dart +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2016, 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. - -import 'dart:async'; - -import "package:async/src/typed/future.dart"; -import "package:test/test.dart"; - -import '../utils.dart'; - -void main() { - group("with valid types, forwards", () { - TypeSafeFuture wrapper; - TypeSafeFuture errorWrapper; - setUp(() { - wrapper = new TypeSafeFuture(new Future.value(12)); - - var error = new Future.error("oh no"); - error.catchError((_) {}); // Don't let the error cause the test to fail. - errorWrapper = new TypeSafeFuture(error); - }); - - test("asStream()", () { - expect(wrapper.asStream().toList(), completion(equals([12]))); - expect(errorWrapper.asStream().first, throwsA("oh no")); - }); - - test("catchError()", () { - expect( - wrapper.catchError(expectAsync1((_) {}, count: 0), - test: expectAsync1((_) {}, count: 0)), - completion(equals(12))); - - expect( - errorWrapper.catchError(expectAsync1((error) { - expect(error, equals("oh no")); - return 42; - }), test: expectAsync1((error) { - expect(error, equals("oh no")); - return true; - })), - completion(equals(42))); - }); - - test("then()", () { - expect( - wrapper.then((value) => value.toString()), completion(equals("12"))); - expect( - errorWrapper.then(expectAsync1((_) {}, count: 0)), throwsA("oh no")); - }); - - test("whenComplete()", () { - expect(wrapper.whenComplete(expectAsync0(() {})), completion(equals(12))); - expect(errorWrapper.whenComplete(expectAsync0(() {})), throwsA("oh no")); - }); - - test("timeout()", () { - expect(wrapper.timeout(new Duration(seconds: 1)), completion(equals(12))); - expect(errorWrapper.timeout(new Duration(seconds: 1)), throwsA("oh no")); - - expect( - new TypeSafeFuture(new Completer().future) - .timeout(Duration.zero), - throwsA(new TypeMatcher())); - - expect( - new TypeSafeFuture(new Completer().future) - .timeout(Duration.zero, onTimeout: expectAsync0(() => 15)), - completion(equals(15))); - }); - }); - - group("with invalid types", () { - TypeSafeFuture wrapper; - setUp(() { - wrapper = new TypeSafeFuture(new Future.value("foo")); - }); - - group("throws a CastError for", () { - test("asStream()", () { - expect(wrapper.asStream().first, throwsCastError); - }); - - test("then()", () { - expect( - wrapper.then(expectAsync1((_) {}, count: 0), - onError: expectAsync1((_) {}, count: 0)), - throwsCastError); - }); - - test("whenComplete()", () { - expect(wrapper.whenComplete(expectAsync0(() {})).then((_) {}), - throwsCastError); - }); - - test("timeout()", () { - expect(wrapper.timeout(new Duration(seconds: 3)).then((_) {}), - throwsCastError); - - expect( - new TypeSafeFuture(new Completer().future) - .timeout(Duration.zero, onTimeout: expectAsync0(() => "foo")) - .then((_) {}), - throwsCastError); - }); - }); - }); -} From 7bfb3771f7c98ee874ea689844b0396a98ee4e9f Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Mon, 9 Jul 2018 16:22:23 -0700 Subject: [PATCH 117/260] Do even less to "fix" `invalidate`. Return `Null`, which is awaitable. --- pkgs/async/lib/src/async_cache.dart | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index c2d7c011..36dec017 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -37,13 +37,6 @@ class AsyncCache { /// Fires when the cache should be considered stale. Timer _stale; - /// Creates a cache that invalidates after an in-flight request is complete. - /// - /// An ephemeral cache guarantees that a callback function will only be - /// executed at most once concurrently. This is useful for requests for which - /// data is updated frequently but stale data is acceptable. - factory AsyncCache.ephemeral() => new AsyncCache(Duration.zero); - /// Creates a cache that invalidates its contents after [duration] has passed. /// /// The [duration] starts counting after the Future returned by [fetch] @@ -51,6 +44,13 @@ class AsyncCache { /// event. AsyncCache(this._duration); + /// Creates a cache that invalidates after an in-flight request is complete. + /// + /// An ephemeral cache guarantees that a callback function will only be + /// executed at most once concurrently. This is useful for requests for which + /// data is updated frequently but stale data is acceptable. + factory AsyncCache.ephemeral() => new AsyncCache(Duration.zero); + /// Returns a cached value from a previous call to [fetch], or runs [callback] /// to compute a new one. /// @@ -89,13 +89,16 @@ class AsyncCache { } /// Removes any cached value. - Future invalidate() { + Null invalidate() { _cachedValueFuture = null; - Future invalidate = _cachedStreamSplitter?.close(); + // TODO: This does not await, but probably should. + _cachedStreamSplitter?.close(); _cachedStreamSplitter = null; _stale?.cancel(); _stale = null; - return invalidate; + + // TODO: This does not return a future, but probably should. + return null; } void _startStaleTimer() { From 55101ecca956dbf889a80076d9223285744c5137 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 13 Jul 2018 16:38:55 -0700 Subject: [PATCH 118/260] Fix awaits only, making cache.invalidate return a future can be separate --- pkgs/async/test/async_cache_test.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 0a5e0f06..a4f452f6 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -58,9 +58,9 @@ void main() { var timesCalled = 0; call() async => 'Called ${++timesCalled}'; expect(await cache.fetch(call), 'Called 1'); - await cache.invalidate(); + cache.invalidate(); expect(await cache.fetch(call), 'Called 2'); - await cache.invalidate(); + cache.invalidate(); expect(await cache.fetch(call), 'Called 3'); }); @@ -129,9 +129,9 @@ void main() { } expect(await cache.fetchStream(call).toList(), ['Called 1']); - await cache.invalidate(); + cache.invalidate(); expect(await cache.fetchStream(call).toList(), ['Called 2']); - await cache.invalidate(); + cache.invalidate(); expect(await cache.fetchStream(call).toList(), ['Called 3']); }); From 0427761b54e4643f9cf06a919cfa27ccc29ab2c4 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 13 Jul 2018 16:39:30 -0700 Subject: [PATCH 119/260] Fix awaits only, making cache.invalidate return a future can be separate --- pkgs/async/lib/src/async_cache.dart | 6 ++---- pkgs/async/test/stream_queue_test.dart | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 36dec017..53084ca3 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -89,16 +89,14 @@ class AsyncCache { } /// Removes any cached value. - Null invalidate() { + void invalidate() { + // TODO: This does not return a future, but probably should. _cachedValueFuture = null; // TODO: This does not await, but probably should. _cachedStreamSplitter?.close(); _cachedStreamSplitter = null; _stale?.cancel(); _stale = null; - - // TODO: This does not return a future, but probably should. - return null; } void _startStaleTimer() { diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 37dedb94..b4600e22 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -153,8 +153,8 @@ main() { test("multiple requests at the same time", () async { var events = new StreamQueue(createStream()); - var result = await Future.wait( - [events.next, events.next, events.next, events.next]); + var result = await Future + .wait([events.next, events.next, events.next, events.next]); expect(result, [1, 2, 3, 4]); await events.cancel(); }); From 3e9b2b9ea6dab9362c3d1a8cc9f1a2c1f59df14a Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 13 Jul 2018 16:54:18 -0700 Subject: [PATCH 120/260] Format stream_queue_test.dart --- pkgs/async/test/stream_queue_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index b4600e22..37dedb94 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -153,8 +153,8 @@ main() { test("multiple requests at the same time", () async { var events = new StreamQueue(createStream()); - var result = await Future - .wait([events.next, events.next, events.next, events.next]); + var result = await Future.wait( + [events.next, events.next, events.next, events.next]); expect(result, [1, 2, 3, 4]); await events.cancel(); }); From 3e24cea826e5fb438aa1a83bc1fb230081b83ee3 Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Thu, 19 Jul 2018 19:25:23 -0400 Subject: [PATCH 121/260] Allow SDK versions <3.0.0 (dart-lang/async#67) --- pkgs/async/CHANGELOG.md | 1 + pkgs/async/analysis_options.yaml | 1 - pkgs/async/pubspec.yaml | 10 +++++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 009aed7c..649b5a17 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,6 @@ ## 2.0.8 +* Set max SDK version to `<3.0.0`. * Deprecate `DelegatingFuture.typed`, it is not necessary in Dart 2. ## 2.0.7 diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 80d23681..24a615cc 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,5 +1,4 @@ analyzer: - strong-mode: true errors: todo: ignore linter: diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 02800df0..7ca4544a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,12 +1,16 @@ name: async -version: 2.0.8-dev -author: Dart Team +version: 2.0.8 + description: Utility functions and classes related to the 'dart:async' library. +author: Dart Team homepage: https://www.github.com/dart-lang/async + environment: - sdk: ">=2.0.0-dev.23.0 <2.0.0" + sdk: '>=2.0.0-dev.23.0 <3.0.0' + dependencies: collection: ^1.5.0 + dev_dependencies: fake_async: ^1.0.0 stack_trace: ^1.0.0 From 56180093fc1825bc9c6742b60e274048c1c49637 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 8 Oct 2018 13:26:30 -0700 Subject: [PATCH 122/260] support the latest pkg:build_runner (dart-lang/async#69) --- pkgs/async/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 7ca4544a..f7f659d6 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.8 +version: 2.0.9-dev description: Utility functions and classes related to the 'dart:async' library. author: Dart Team @@ -16,6 +16,6 @@ dev_dependencies: stack_trace: ^1.0.0 test: ^1.0.0 # For building and testing with DDC - build_runner: ^0.9.0 + build_runner: ^1.0.0 build_web_compilers: ^0.4.0 build_test: ^0.10.1 From 0f55ca24d0c7d8fae94789d250d75bb8830e93b7 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Dec 2018 19:48:53 -0800 Subject: [PATCH 123/260] dev dep: latest build_web_compilers (dart-lang/async#71) --- pkgs/async/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index f7f659d6..582663a6 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -17,5 +17,5 @@ dev_dependencies: test: ^1.0.0 # For building and testing with DDC build_runner: ^1.0.0 - build_web_compilers: ^0.4.0 + build_web_compilers: ^1.0.0 build_test: ^0.10.1 From 9a51638293020b2ffa841ab9569a38166e420852 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 25 Jan 2019 01:23:22 -0800 Subject: [PATCH 124/260] Merge _StreamQueue into StreamQueue (dart-lang/async#73) The original reason to split into an abstract and concrete class was to allow other implementations that would allow for the transaction implementation. Since that was implemented without a separate subclass merge the implementation back to one class to avoid unnecessary abstraction and make it easier to debug. --- pkgs/async/lib/src/stream_queue.dart | 132 ++++++++++++--------------- 1 file changed, 58 insertions(+), 74 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 0c81aec8..61a16545 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -62,7 +62,7 @@ import "stream_splitter.dart"; /// /// When you need no further events the `StreamQueue` should be closed /// using [cancel]. This releases the underlying stream subscription. -abstract class StreamQueue { +class StreamQueue { // This class maintains two queues: one of events and one of requests. // The active request (the one in front of the queue) is called with // the current event queue when it becomes active, every time a @@ -81,6 +81,14 @@ abstract class StreamQueue { // potentially a request that takes either five or zero events, determined // by the content of the fifth event. + final Stream _source; + + /// Subscription on [_source] while listening for events. + /// + /// Set to subscription when listening, and set to `null` when the + /// subscription is done (and [_isDone] is set to true). + StreamSubscription _subscription; + /// Whether the event source is done. bool _isDone = false; @@ -107,9 +115,10 @@ abstract class StreamQueue { final Queue<_EventRequest> _requestQueue = new Queue(); /// Create a `StreamQueue` of the events of [source]. - factory StreamQueue(Stream source) = _StreamQueue; + factory StreamQueue(Stream source) => StreamQueue._(source); - StreamQueue._(); + // Private generative constructor to avoid subclasses. + StreamQueue._(this._source); /// Asks if the stream has any more events. /// @@ -432,24 +441,66 @@ abstract class StreamQueue { /// Can only be used by the very last request (the stream queue must /// be closed by that request). /// Only used by [rest]. - Stream _extractStream(); + Stream _extractStream() { + assert(_isClosed); + if (_isDone) { + return new Stream.empty(); + } + _isDone = true; + + if (_subscription == null) { + return _source; + } + + var subscription = _subscription; + _subscription = null; + + var wasPaused = subscription.isPaused; + var result = new SubscriptionStream(subscription); + // Resume after creating stream because that pauses the subscription too. + // This way there won't be a short resumption in the middle. + if (wasPaused) subscription.resume(); + return result; + } /// Requests that the event source pauses events. /// /// This is called automatically when the request queue is empty. /// /// The event source is restarted by the next call to [_ensureListening]. - void _pause(); + void _pause() { + _subscription.pause(); + } /// Ensures that we are listening on events from the event source. /// /// Starts listening for the first time or resumes after a [_pause]. /// /// Is called automatically if a request requires more events. - void _ensureListening(); + void _ensureListening() { + if (_isDone) return; + if (_subscription == null) { + _subscription = _source.listen((data) { + _addResult(new Result.value(data)); + }, onError: (error, StackTrace stackTrace) { + _addResult(new Result.error(error, stackTrace)); + }, onDone: () { + _subscription = null; + this._close(); + }); + } else { + _subscription.resume(); + } + } /// Cancels the underlying event source. - Future _cancel(); + Future _cancel() { + if (_isDone) return null; + _subscription ??= _source.listen(null); + var future = _subscription.cancel(); + _close(); + return future; + } // ------------------------------------------------------------------ // Methods called by the event source to add events or say that it's @@ -494,73 +545,6 @@ abstract class StreamQueue { } } -/// The default implementation of [StreamQueue]. -/// -/// This queue gets its events from a stream which is listened -/// to when a request needs events. -class _StreamQueue extends StreamQueue { - /// Source of events. - final Stream _sourceStream; - - /// Subscription on [_sourceStream] while listening for events. - /// - /// Set to subscription when listening, and set to `null` when the - /// subscription is done (and [_isDone] is set to true). - StreamSubscription _subscription; - - _StreamQueue(this._sourceStream) : super._(); - - Future _cancel() { - if (_isDone) return null; - if (_subscription == null) _subscription = _sourceStream.listen(null); - var future = _subscription.cancel(); - _close(); - return future; - } - - void _ensureListening() { - if (_isDone) return; - if (_subscription == null) { - _subscription = _sourceStream.listen((data) { - _addResult(new Result.value(data)); - }, onError: (error, StackTrace stackTrace) { - _addResult(new Result.error(error, stackTrace)); - }, onDone: () { - _subscription = null; - this._close(); - }); - } else { - _subscription.resume(); - } - } - - void _pause() { - _subscription.pause(); - } - - Stream _extractStream() { - assert(_isClosed); - if (_isDone) { - return new Stream.empty(); - } - _isDone = true; - - if (_subscription == null) { - return _sourceStream; - } - - var subscription = _subscription; - _subscription = null; - - var wasPaused = subscription.isPaused; - var result = new SubscriptionStream(subscription); - // Resume after creating stream because that pauses the subscription too. - // This way there won't be a short resumption in the middle. - if (wasPaused) subscription.resume(); - return result; - } -} - /// A transaction on a [StreamQueue], created by [StreamQueue.startTransaction]. /// /// Copies of the parent queue may be created using [newQueue]. Calling [commit] From 053559b8ebce421c976cf2b1b0030cde48b70e0f Mon Sep 17 00:00:00 2001 From: Dwayne Slater Date: Fri, 25 Jan 2019 06:26:50 -0500 Subject: [PATCH 125/260] Fix CancelableOperation.valueOrCancellation's type signature (dart-lang/async#68) * Fix CancelableOperation.valueOrCancellation's type signature * Increment minor version, add changelog entry --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/cancelable_operation.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 649b5a17..c71ac913 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.0 + +* Fix `CancelableOperation.valueOrCancellation`'s type signature + ## 2.0.8 * Set max SDK version to `<3.0.0`. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 2bf0f3b6..0e010dad 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -63,7 +63,7 @@ class CancelableOperation { /// If this operation completes, this completes to the same result as [value]. /// If this operation is cancelled, the returned future waits for the future /// returned by [cancel], then completes to [cancellationValue]. - Future valueOrCancellation([T cancellationValue]) { + Future valueOrCancellation([T cancellationValue]) { var completer = new Completer.sync(); value.then((result) => completer.complete(result), onError: completer.completeError); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 582663a6..37db6601 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.0.9-dev +version: 2.1.0 description: Utility functions and classes related to the 'dart:async' library. author: Dart Team From b39524c851993c7fe6e5f3b298c066a763efab38 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 25 Jan 2019 17:39:35 -0800 Subject: [PATCH 126/260] Run dartfmt --fix to drop unnecessary new/const (dart-lang/async#74) Bump minimum SDK version in pubspec to 2.0.0 --- pkgs/async/analysis_options.yaml | 2 + pkgs/async/lib/src/async_cache.dart | 12 +- pkgs/async/lib/src/async_memoizer.dart | 4 +- pkgs/async/lib/src/byte_collector.dart | 6 +- pkgs/async/lib/src/cancelable_operation.dart | 18 +- pkgs/async/lib/src/delegate/event_sink.dart | 2 +- pkgs/async/lib/src/delegate/sink.dart | 2 +- .../lib/src/delegate/stream_consumer.dart | 2 +- pkgs/async/lib/src/delegate/stream_sink.dart | 2 +- .../lib/src/delegate/stream_subscription.dart | 2 +- pkgs/async/lib/src/future_group.dart | 8 +- pkgs/async/lib/src/lazy_stream.dart | 4 +- pkgs/async/lib/src/null_stream_sink.dart | 10 +- pkgs/async/lib/src/restartable_timer.dart | 6 +- pkgs/async/lib/src/result/capture_sink.dart | 4 +- .../lib/src/result/capture_transformer.dart | 4 +- pkgs/async/lib/src/result/error.dart | 2 +- .../lib/src/result/release_transformer.dart | 4 +- pkgs/async/lib/src/result/result.dart | 38 ++-- pkgs/async/lib/src/result/value.dart | 2 +- .../src/single_subscription_transformer.dart | 4 +- pkgs/async/lib/src/stream_completer.dart | 12 +- pkgs/async/lib/src/stream_group.dart | 18 +- pkgs/async/lib/src/stream_queue.dart | 88 ++++---- pkgs/async/lib/src/stream_sink_completer.dart | 12 +- .../lib/src/stream_sink_transformer.dart | 4 +- .../handler_transformer.dart | 12 +- .../stream_transformer_wrapper.dart | 4 +- pkgs/async/lib/src/stream_splitter.dart | 18 +- .../src/stream_subscription_transformer.dart | 12 +- pkgs/async/lib/src/stream_zip.dart | 6 +- pkgs/async/lib/src/subscription_stream.dart | 4 +- .../lib/src/typed_stream_transformer.dart | 2 +- pkgs/async/lib/src/utils.dart | 4 +- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/async_cache_test.dart | 24 +-- pkgs/async/test/async_memoizer_test.dart | 2 +- pkgs/async/test/byte_collection_test.dart | 24 +-- .../async/test/cancelable_operation_test.dart | 60 +++--- pkgs/async/test/future_group_test.dart | 40 ++-- pkgs/async/test/lazy_stream_test.dart | 24 +-- pkgs/async/test/null_stream_sink_test.dart | 38 ++-- pkgs/async/test/restartable_timer_test.dart | 53 +++-- .../test/result/result_captureAll_test.dart | 40 ++-- .../test/result/result_flattenAll_test.dart | 4 +- .../async/test/result/result_future_test.dart | 6 +- pkgs/async/test/result/result_test.dart | 96 ++++----- .../single_subscription_transformer_test.dart | 4 +- pkgs/async/test/stream_completer_test.dart | 66 +++--- pkgs/async/test/stream_group_test.dart | 117 +++++----- pkgs/async/test/stream_queue_test.dart | 201 +++++++++--------- .../test/stream_sink_completer_test.dart | 60 +++--- .../test/stream_sink_transformer_test.dart | 50 +++-- pkgs/async/test/stream_splitter_test.dart | 6 +- pkgs/async/test/stream_zip_test.dart | 51 +++-- pkgs/async/test/stream_zip_zone_test.dart | 12 +- pkgs/async/test/subscription_stream_test.dart | 28 +-- .../test/subscription_transformer_test.dart | 30 +-- .../stream_subscription_test.dart | 16 +- pkgs/async/test/utils.dart | 22 +- 60 files changed, 696 insertions(+), 714 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 24a615cc..82ef545b 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -4,3 +4,5 @@ analyzer: linter: rules: - prefer_typing_uninitialized_variables + - unnecessary_new + - unnecessary_const diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 53084ca3..6bd34bd2 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -49,7 +49,7 @@ class AsyncCache { /// An ephemeral cache guarantees that a callback function will only be /// executed at most once concurrently. This is useful for requests for which /// data is updated frequently but stale data is acceptable. - factory AsyncCache.ephemeral() => new AsyncCache(Duration.zero); + factory AsyncCache.ephemeral() => AsyncCache(Duration.zero); /// Returns a cached value from a previous call to [fetch], or runs [callback] /// to compute a new one. @@ -58,7 +58,7 @@ class AsyncCache { /// value. Otherwise, runs [callback] and returns its new return value. Future fetch(Future callback()) async { if (_cachedStreamSplitter != null) { - throw new StateError('Previously used to cache via `fetchStream`'); + throw StateError('Previously used to cache via `fetchStream`'); } if (_cachedValueFuture == null) { _cachedValueFuture = callback(); @@ -76,11 +76,11 @@ class AsyncCache { /// return value. Stream fetchStream(Stream callback()) { if (_cachedValueFuture != null) { - throw new StateError('Previously used to cache via `fetch`'); + throw StateError('Previously used to cache via `fetch`'); } if (_cachedStreamSplitter == null) { - _cachedStreamSplitter = new StreamSplitter(callback() - .transform(new StreamTransformer.fromHandlers(handleDone: (sink) { + _cachedStreamSplitter = StreamSplitter(callback() + .transform(StreamTransformer.fromHandlers(handleDone: (sink) { _startStaleTimer(); sink.close(); }))); @@ -100,6 +100,6 @@ class AsyncCache { } void _startStaleTimer() { - _stale = new Timer(_duration, invalidate); + _stale = Timer(_duration, invalidate); } } diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index d1c0f329..5e834000 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -31,7 +31,7 @@ class AsyncMemoizer { /// /// This can be accessed at any time, and will fire once [runOnce] is called. Future get future => _completer.future; - final _completer = new Completer(); + final _completer = Completer(); /// Whether [runOnce] has been called yet. bool get hasRun => _completer.isCompleted; @@ -40,7 +40,7 @@ class AsyncMemoizer { /// /// If [runOnce] has already been called, this returns the original result. Future runOnce(FutureOr computation()) { - if (!hasRun) _completer.complete(new Future.sync(computation)); + if (!hasRun) _completer.complete(Future.sync(computation)); return future; } } diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart index 3c1d49d2..f01cc2ac 100644 --- a/pkgs/async/lib/src/byte_collector.dart +++ b/pkgs/async/lib/src/byte_collector.dart @@ -31,7 +31,7 @@ CancelableOperation collectBytesCancelable( Stream> source) { return _collectBytes( source, - (subscription, result) => new CancelableOperation.fromFuture(result, + (subscription, result) => CancelableOperation.fromFuture(result, onCancel: subscription.cancel)); } @@ -46,7 +46,7 @@ T _collectBytes( StreamSubscription> subscription, Future result)) { var byteLists = >[]; var length = 0; - var completer = new Completer.sync(); + var completer = Completer.sync(); var subscription = source.listen( (bytes) { byteLists.add(bytes); @@ -62,7 +62,7 @@ T _collectBytes( // Join a lists of bytes with a known total length into a single [Uint8List]. Uint8List _collect(int length, List> byteLists) { - var result = new Uint8List(length); + var result = Uint8List(length); int i = 0; for (var byteList in byteLists) { var end = i + byteList.length; diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 0e010dad..e6115cb5 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -31,7 +31,7 @@ class CancelableOperation { /// It's guaranteed to only be called once. factory CancelableOperation.fromFuture(Future inner, {FutureOr onCancel()}) { - var completer = new CancelableCompleter(onCancel: onCancel); + var completer = CancelableCompleter(onCancel: onCancel); completer.complete(inner); return completer.operation; } @@ -45,7 +45,7 @@ class CancelableOperation { /// canceled, this is as well. Stream asStream() { var controller = - new StreamController(sync: true, onCancel: _completer._cancel); + StreamController(sync: true, onCancel: _completer._cancel); value.then((value) { controller.add(value); @@ -64,7 +64,7 @@ class CancelableOperation { /// If this operation is cancelled, the returned future waits for the future /// returned by [cancel], then completes to [cancellationValue]. Future valueOrCancellation([T cancellationValue]) { - var completer = new Completer.sync(); + var completer = Completer.sync(); value.then((result) => completer.complete(result), onError: completer.completeError); @@ -100,8 +100,8 @@ class CancelableCompleter { /// It's guaranteed to only be called once. CancelableCompleter({FutureOr onCancel()}) : _onCancel = onCancel, - _inner = new Completer() { - _operation = new CancelableOperation._(this); + _inner = Completer() { + _operation = CancelableOperation._(this); } /// The operation controlled by this completer. @@ -117,14 +117,14 @@ class CancelableCompleter { bool _isCanceled = false; /// The memoizer for [_cancel]. - final _cancelMemo = new AsyncMemoizer(); + final _cancelMemo = AsyncMemoizer(); /// Completes [operation] to [value]. /// /// If [value] is a [Future], this will complete to the result of that /// [Future] once it completes. void complete([value]) { - if (_isCompleted) throw new StateError("Operation already completed"); + if (_isCompleted) throw StateError("Operation already completed"); _isCompleted = true; if (value is! Future) { @@ -150,7 +150,7 @@ class CancelableCompleter { /// Completes [operation] to [error]. void completeError(Object error, [StackTrace stackTrace]) { - if (_isCompleted) throw new StateError("Operation already completed"); + if (_isCompleted) throw StateError("Operation already completed"); _isCompleted = true; if (_isCanceled) return; @@ -159,7 +159,7 @@ class CancelableCompleter { /// Cancel the completer. Future _cancel() { - if (_inner.isCompleted) return new Future.value(); + if (_inner.isCompleted) return Future.value(); return _cancelMemo.runOnce(() { _isCanceled = true; diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 54c3e52b..95d78413 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -23,7 +23,7 @@ class DelegatingEventSink implements EventSink { /// [add] may throw a [CastError] if the argument type doesn't match the /// reified type of [sink]. static EventSink typed(EventSink sink) => - sink is EventSink ? sink : new DelegatingEventSink._(sink); + sink is EventSink ? sink : DelegatingEventSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index 7c68a0fb..b8ba76d5 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -21,7 +21,7 @@ class DelegatingSink implements Sink { /// throw a [CastError] if the argument type doesn't match the reified type of /// [sink]. static Sink typed(Sink sink) => - sink is Sink ? sink : new DelegatingSink._(sink); + sink is Sink ? sink : DelegatingSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index ba830408..ad53182e 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -25,7 +25,7 @@ class DelegatingStreamConsumer implements StreamConsumer { static StreamConsumer typed(StreamConsumer consumer) => consumer is StreamConsumer ? consumer - : new DelegatingStreamConsumer._(consumer); + : DelegatingStreamConsumer._(consumer); Future addStream(Stream stream) => _consumer.addStream(stream); diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index 198df8a7..67eb33c4 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -25,7 +25,7 @@ class DelegatingStreamSink implements StreamSink { /// throw a [CastError] if the argument type doesn't match the reified type of /// [sink]. static StreamSink typed(StreamSink sink) => - sink is StreamSink ? sink : new DelegatingStreamSink._(sink); + sink is StreamSink ? sink : DelegatingStreamSink._(sink); void add(T data) { _sink.add(data); diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 6bef924b..07af08eb 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -26,7 +26,7 @@ class DelegatingStreamSubscription implements StreamSubscription { static StreamSubscription typed(StreamSubscription subscription) => subscription is StreamSubscription ? subscription - : new TypeSafeStreamSubscription(subscription); + : TypeSafeStreamSubscription(subscription); void onData(void handleData(T data)) { _source.onData(handleData); diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 0bf3158f..7e174947 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -31,7 +31,7 @@ class FutureGroup implements Sink> { /// This will also complete with an error if any of the futures in the group /// fails, regardless of whether [close] was called. Future> get future => _completer.future; - final _completer = new Completer>(); + final _completer = Completer>(); /// Whether this group has no pending futures. bool get isIdle => _pending == 0; @@ -43,7 +43,7 @@ class FutureGroup implements Sink> { /// called, this stream will close. Stream get onIdle { if (_onIdleController == null) { - _onIdleController = new StreamController.broadcast(sync: true); + _onIdleController = StreamController.broadcast(sync: true); } return _onIdleController.stream; } @@ -54,11 +54,11 @@ class FutureGroup implements Sink> { /// the order they were added. /// /// The slots for futures that haven't completed yet are `null`. - final _values = new List(); + final _values = List(); /// Wait for [task] to complete. void add(Future task) { - if (_closed) throw new StateError("The FutureGroup is closed."); + if (_closed) throw StateError("The FutureGroup is closed."); // Ensure that future values are put into [values] in the same order they're // added to the group by pre-allocating a slot for them and recording its diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index f07c387e..278957ad 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -22,13 +22,13 @@ class LazyStream extends Stream { /// a listener and forwards to the returned stream. LazyStream(FutureOr> callback()) : _callback = callback { // Explicitly check for null because we null out [_callback] internally. - if (_callback == null) throw new ArgumentError.notNull('callback'); + if (_callback == null) throw ArgumentError.notNull('callback'); } StreamSubscription listen(void onData(T event), {Function onError, void onDone(), bool cancelOnError}) { if (_callback == null) { - throw new StateError("Stream has already been listened to."); + throw StateError("Stream has already been listened to."); } // Null out the callback before we invoke it to ensure that even while diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart index c83790ca..c9d3d5d7 100644 --- a/pkgs/async/lib/src/null_stream_sink.dart +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -42,13 +42,13 @@ class NullStreamSink implements StreamSink { /// /// If [done] is passed, it's used as the [Sink.done] future. Otherwise, a /// completed future is used. - NullStreamSink({Future done}) : done = done ?? new Future.value(); + NullStreamSink({Future done}) : done = done ?? Future.value(); /// Creates a null sink whose [done] future emits [error]. /// /// Note that this error will not be considered uncaught. NullStreamSink.error(error, [StackTrace stackTrace]) - : done = new Future.error(error, stackTrace) + : done = Future.error(error, stackTrace) // Don't top-level the error. This gives the user a change to call // [close] or [done], and matches the behavior of a remote endpoint // experiencing an error. @@ -66,7 +66,7 @@ class NullStreamSink implements StreamSink { _checkEventAllowed(); _addingStream = true; - var future = stream.listen(null).cancel() ?? new Future.value(); + var future = stream.listen(null).cancel() ?? Future.value(); return future.whenComplete(() { _addingStream = false; }); @@ -75,9 +75,9 @@ class NullStreamSink implements StreamSink { /// Throws a [StateError] if [close] has been called or an [addStream] call is /// pending. void _checkEventAllowed() { - if (_closed) throw new StateError("Cannot add to a closed sink."); + if (_closed) throw StateError("Cannot add to a closed sink."); if (_addingStream) { - throw new StateError("Cannot add to a sink while adding a stream."); + throw StateError("Cannot add to a sink while adding a stream."); } } diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart index 7b058212..b8e414ad 100644 --- a/pkgs/async/lib/src/restartable_timer.dart +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -26,7 +26,7 @@ class RestartableTimer implements Timer { /// The [callback] function is invoked after the given [duration]. Unlike a /// normal non-periodic [Timer], [callback] may be called more than once. RestartableTimer(this._duration, this._callback) { - _timer = new Timer(_duration, _callback); + _timer = Timer(_duration, _callback); } bool get isActive => _timer.isActive; @@ -37,7 +37,7 @@ class RestartableTimer implements Timer { /// This restarts the timer even if it has already fired or has been canceled. void reset() { _timer.cancel(); - _timer = new Timer(_duration, _callback); + _timer = Timer(_duration, _callback); } void cancel() { @@ -49,6 +49,6 @@ class RestartableTimer implements Timer { // See https://github.com/dart-lang/sdk/issues/31664 // ignore: override_on_non_overriding_getter int get tick { - throw new UnimplementedError("tick"); + throw UnimplementedError("tick"); } } diff --git a/pkgs/async/lib/src/result/capture_sink.dart b/pkgs/async/lib/src/result/capture_sink.dart index 742c1601..0477a19e 100644 --- a/pkgs/async/lib/src/result/capture_sink.dart +++ b/pkgs/async/lib/src/result/capture_sink.dart @@ -13,11 +13,11 @@ class CaptureSink implements EventSink { CaptureSink(EventSink> sink) : _sink = sink; void add(T value) { - _sink.add(new Result.value(value)); + _sink.add(Result.value(value)); } void addError(Object error, [StackTrace stackTrace]) { - _sink.add(new Result.error(error, stackTrace)); + _sink.add(Result.error(error, stackTrace)); } void close() { diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index b9a538ab..a727a8e1 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -15,6 +15,6 @@ class CaptureStreamTransformer extends StreamTransformerBase> { const CaptureStreamTransformer(); Stream> bind(Stream source) => - new Stream>.eventTransformed( - source, (sink) => new CaptureSink(sink)); + Stream>.eventTransformed( + source, (sink) => CaptureSink(sink)); } diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index 2feb2c25..8f29d130 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -30,7 +30,7 @@ class ErrorResult implements Result { sink.addError(error, stackTrace); } - Future get asFuture => new Future.error(error, stackTrace); + Future get asFuture => Future.error(error, stackTrace); /// Calls an error handler with the error and stacktrace. /// diff --git a/pkgs/async/lib/src/result/release_transformer.dart b/pkgs/async/lib/src/result/release_transformer.dart index 01865cc9..1aead30b 100644 --- a/pkgs/async/lib/src/result/release_transformer.dart +++ b/pkgs/async/lib/src/result/release_transformer.dart @@ -12,9 +12,9 @@ class ReleaseStreamTransformer extends StreamTransformerBase, T> { const ReleaseStreamTransformer(); Stream bind(Stream> source) { - return new Stream.eventTransformed(source, _createSink); + return Stream.eventTransformed(source, _createSink); } // Since Stream.eventTransformed is not generic, this method can be static. - static EventSink _createSink(EventSink sink) => new ReleaseSink(sink); + static EventSink _createSink(EventSink sink) => ReleaseSink(sink); } diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 25503b57..28799fae 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -28,14 +28,14 @@ abstract class Result { /// The result of the transformation is a stream of [Result] values and no /// error events. This is the transformer used by [captureStream]. static const StreamTransformer> - captureStreamTransformer = const CaptureStreamTransformer(); + captureStreamTransformer = CaptureStreamTransformer(); /// A stream transformer that releases a stream of result events. /// /// The result of the transformation is a stream of values and error events. /// This is the transformer used by [releaseStream]. static const StreamTransformer, Object> - releaseStreamTransformer = const ReleaseStreamTransformer(); + releaseStreamTransformer = ReleaseStreamTransformer(); /// A sink transformer that captures events into [Result]s. /// @@ -43,8 +43,8 @@ abstract class Result { /// values and no error events. static const StreamSinkTransformer> captureSinkTransformer = - const StreamSinkTransformer>.fromStreamTransformer( - const CaptureStreamTransformer()); + StreamSinkTransformer>.fromStreamTransformer( + CaptureStreamTransformer()); /// A sink transformer that releases result events. /// @@ -52,8 +52,8 @@ abstract class Result { /// error events. static const StreamSinkTransformer, Object> releaseSinkTransformer = - const StreamSinkTransformer, Object>.fromStreamTransformer( - const ReleaseStreamTransformer()); + StreamSinkTransformer, Object>.fromStreamTransformer( + ReleaseStreamTransformer()); /// Creates a `Result` with the result of calling [computation]. /// @@ -62,9 +62,9 @@ abstract class Result { /// the call. factory Result(T computation()) { try { - return new ValueResult(computation()); + return ValueResult(computation()); } catch (e, s) { - return new ErrorResult(e, s); + return ErrorResult(e, s); } } @@ -77,15 +77,15 @@ abstract class Result { /// /// Alias for [ErrorResult.ErrorResult]. factory Result.error(Object error, [StackTrace stackTrace]) => - new ErrorResult(error, stackTrace); + ErrorResult(error, stackTrace); /// Captures the result of a future into a `Result` future. /// /// The resulting future will never have an error. /// Errors have been converted to an [ErrorResult] value. static Future> capture(Future future) { - return future.then((value) => new ValueResult(value), - onError: (error, stackTrace) => new ErrorResult(error, stackTrace)); + return future.then((value) => ValueResult(value), + onError: (error, stackTrace) => ErrorResult(error, stackTrace)); } /// Captures each future in [elements], @@ -111,13 +111,13 @@ abstract class Result { } }); } else { - results.add(new Result.value(element)); + results.add(Result.value(element)); } } if (pending == 0) { - return new Future>>.value(results); + return Future>>.value(results); } - completer = new Completer>>(); + completer = Completer>>(); return completer.future; } @@ -136,7 +136,7 @@ abstract class Result { /// The returned stream will not have any error events. /// Errors from the source stream have been converted to [ErrorResult]s. static Stream> captureStream(Stream source) => - source.transform(new CaptureStreamTransformer()); + source.transform(CaptureStreamTransformer()); /// Releases a stream of [result] values into a stream of the results. /// @@ -144,7 +144,7 @@ abstract class Result { /// the returned stream as appropriate. /// Errors from the source stream become errors in the returned stream. static Stream releaseStream(Stream> source) => - source.transform(new ReleaseStreamTransformer()); + source.transform(ReleaseStreamTransformer()); /// Releases results added to the returned sink as data and errors on [sink]. /// @@ -152,7 +152,7 @@ abstract class Result { /// on [sink]. Errors added to the returned sink are forwarded directly to /// [sink] and so is the [EventSink.close] calls. static EventSink> releaseSink(EventSink sink) => - new ReleaseSink(sink); + ReleaseSink(sink); /// Captures the events of the returned sink into results on [sink]. /// @@ -162,7 +162,7 @@ abstract class Result { /// /// When the returned sink is closed, so is [sink]. static EventSink captureSink(EventSink> sink) => - new CaptureSink(sink); + CaptureSink(sink); /// Converts a result of a result to a single result. /// @@ -188,7 +188,7 @@ abstract class Result { return result.asError; } } - return new Result>.value(values); + return Result>.value(values); } /// Whether this result is a value result. diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart index edc41fb4..dddc9fe4 100644 --- a/pkgs/async/lib/src/result/value.dart +++ b/pkgs/async/lib/src/result/value.dart @@ -27,7 +27,7 @@ class ValueResult implements Result { sink.add(value); } - Future get asFuture => new Future.value(value); + Future get asFuture => Future.value(value); int get hashCode => value.hashCode ^ 0x323f1d61; diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index ff171899..8007a42c 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -18,8 +18,8 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { Stream bind(Stream stream) { StreamSubscription subscription; - var controller = new StreamController( - sync: true, onCancel: () => subscription.cancel()); + var controller = + StreamController(sync: true, onCancel: () => subscription.cancel()); subscription = stream.listen((value) { // TODO(nweiz): When we release a new major version, get rid of the second // type parameter and avoid this conversion. diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index 4311de52..bbb21559 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -24,7 +24,7 @@ import "dart:async"; /// the listen is performed directly on the source stream. class StreamCompleter { /// The stream doing the actual work, is returned by [stream]. - final _stream = new _CompleterStream(); + final _stream = _CompleterStream(); /// Convert a `Future` to a `Stream`. /// @@ -35,7 +35,7 @@ class StreamCompleter { /// If the future completes with an error, the returned stream will /// instead contain just that error. static Stream fromFuture(Future> streamFuture) { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); streamFuture.then(completer.setSourceStream, onError: completer.setError); return completer.stream; } @@ -75,7 +75,7 @@ class StreamCompleter { /// most once. Trying to call any of them again will fail. void setSourceStream(Stream sourceStream) { if (_stream._isSourceStreamSet) { - throw new StateError("Source stream already set"); + throw StateError("Source stream already set"); } _stream._setSourceStream(sourceStream); } @@ -86,7 +86,7 @@ class StreamCompleter { /// most once. Trying to call any of them again will fail. void setEmpty() { if (_stream._isSourceStreamSet) { - throw new StateError("Source stream already set"); + throw StateError("Source stream already set"); } _stream._setEmpty(); } @@ -98,7 +98,7 @@ class StreamCompleter { /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at /// most once. Trying to call any of them again will fail. void setError(error, [StackTrace stackTrace]) { - setSourceStream(new Stream.fromFuture(new Future.error(error, stackTrace))); + setSourceStream(Stream.fromFuture(Future.error(error, stackTrace))); } } @@ -179,6 +179,6 @@ class _CompleterStream extends Stream { // Creates the [_controller]. void _createController() { assert(_controller == null); - _controller = new StreamController(sync: true); + _controller = StreamController(sync: true); } } diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 6361a5cb..d647ce30 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -47,14 +47,14 @@ class StreamGroup implements Sink> { /// subscriptions will be canceled and set to null again. Single-subscriber /// stream subscriptions will be left intact, since they can't be /// re-subscribed. - final _subscriptions = new Map, StreamSubscription>(); + final _subscriptions = Map, StreamSubscription>(); /// Merges the events from [streams] into a single (single-subscriber) stream. /// /// This is equivalent to adding [streams] to a group, closing that group, and /// returning its stream. static Stream merge(Iterable> streams) { - var group = new StreamGroup(); + var group = StreamGroup(); streams.forEach(group.add); group.close(); return group.stream; @@ -62,7 +62,7 @@ class StreamGroup implements Sink> { /// Creates a new stream group where [stream] is single-subscriber. StreamGroup() { - _controller = new StreamController( + _controller = StreamController( onListen: _onListen, onPause: _onPause, onResume: _onResume, @@ -72,7 +72,7 @@ class StreamGroup implements Sink> { /// Creates a new stream group where [stream] is a broadcast stream. StreamGroup.broadcast() { - _controller = new StreamController.broadcast( + _controller = StreamController.broadcast( onListen: _onListen, onCancel: _onCancelBroadcast, sync: true); } @@ -90,7 +90,7 @@ class StreamGroup implements Sink> { /// Throws a [StateError] if this group is closed. Future add(Stream stream) { if (_closed) { - throw new StateError("Can't add a Stream to a closed StreamGroup."); + throw StateError("Can't add a Stream to a closed StreamGroup."); } if (_state == _StreamGroupState.dormant) { @@ -219,12 +219,12 @@ class _StreamGroupState { /// /// New streams added to the group will be listened once the group has a /// listener. - static const dormant = const _StreamGroupState("dormant"); + static const dormant = _StreamGroupState("dormant"); /// The group has one or more listeners and is actively firing events. /// /// New streams added to the group will be immediately listeners. - static const listening = const _StreamGroupState("listening"); + static const listening = _StreamGroupState("listening"); /// The group is paused and no more events will be fired until it resumes. /// @@ -232,7 +232,7 @@ class _StreamGroupState { /// will be resumed once the group itself is resumed. /// /// This state is only used by single-subscriber groups. - static const paused = const _StreamGroupState("paused"); + static const paused = _StreamGroupState("paused"); /// The group is canceled and no more events will be fired ever. /// @@ -240,7 +240,7 @@ class _StreamGroupState { /// discarded. /// /// This state is only used by single-subscriber groups. - static const canceled = const _StreamGroupState("canceled"); + static const canceled = _StreamGroupState("canceled"); /// The name of the state. /// diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 61a16545..fd3186e2 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -44,7 +44,7 @@ import "stream_splitter.dart"; /// /// Example: /// -/// var events = new StreamQueue(someStreamOfLines); +/// var events = StreamQueue(someStreamOfLines); /// var first = await events.next; /// while (first.startsWith('#')) { /// // Skip comments. @@ -107,12 +107,12 @@ class StreamQueue { var _eventsReceived = 0; /// Queue of events not used by a request yet. - final QueueList> _eventQueue = new QueueList(); + final QueueList> _eventQueue = QueueList(); /// Queue of pending requests. /// /// Access through methods below to ensure consistency. - final Queue<_EventRequest> _requestQueue = new Queue(); + final Queue<_EventRequest> _requestQueue = Queue(); /// Create a `StreamQueue` of the events of [source]. factory StreamQueue(Stream source) => StreamQueue._(source); @@ -133,7 +133,7 @@ class StreamQueue { /// one events. Future get hasNext { if (!_isClosed) { - var hasNextRequest = new _HasNextRequest(); + var hasNextRequest = _HasNextRequest(); _addRequest(hasNextRequest); return hasNextRequest.future; } @@ -146,9 +146,9 @@ class StreamQueue { /// If one of the next [count] events is an error, the returned future /// completes with this error, and the error is still left in the queue. Future> lookAhead(int count) { - if (count < 0) throw new RangeError.range(count, 0, null, "count"); + if (count < 0) throw RangeError.range(count, 0, null, "count"); if (!_isClosed) { - var request = new _LookAheadRequest(count); + var request = _LookAheadRequest(count); _addRequest(request); return request.future; } @@ -171,7 +171,7 @@ class StreamQueue { /// first events that were not consumed by previous requeusts. Future get next { if (!_isClosed) { - var nextRequest = new _NextRequest(); + var nextRequest = _NextRequest(); _addRequest(nextRequest); return nextRequest.future; } @@ -184,7 +184,7 @@ class StreamQueue { /// If the next event is an error event, it stays in the queue. Future get peek { if (!_isClosed) { - var nextRequest = new _PeekRequest(); + var nextRequest = _PeekRequest(); _addRequest(nextRequest); return nextRequest.future; } @@ -204,7 +204,7 @@ class StreamQueue { if (_isClosed) { throw _failClosed(); } - var request = new _RestRequest(this); + var request = _RestRequest(this); _isClosed = true; _addRequest(request); return request.stream; @@ -226,9 +226,9 @@ class StreamQueue { /// then all events were succssfully skipped. If the value /// is greater than zero then the stream ended early. Future skip(int count) { - if (count < 0) throw new RangeError.range(count, 0, null, "count"); + if (count < 0) throw RangeError.range(count, 0, null, "count"); if (!_isClosed) { - var request = new _SkipRequest(count); + var request = _SkipRequest(count); _addRequest(request); return request.future; } @@ -251,9 +251,9 @@ class StreamQueue { /// of data collected so far. That is, the returned /// list may have fewer than [count] elements. Future> take(int count) { - if (count < 0) throw new RangeError.range(count, 0, null, "count"); + if (count < 0) throw RangeError.range(count, 0, null, "count"); if (!_isClosed) { - var request = new _TakeRequest(count); + var request = _TakeRequest(count); _addRequest(request); return request.future; } @@ -295,7 +295,7 @@ class StreamQueue { StreamQueueTransaction startTransaction() { if (_isClosed) throw _failClosed(); - var request = new _TransactionRequest(this); + var request = _TransactionRequest(this); _addRequest(request); return request.transaction; } @@ -353,7 +353,7 @@ class StreamQueue { /// See also [startTransaction] and [withTransaction]. /// /// ```dart - /// final _stdinQueue = new StreamQueue(stdin); + /// final _stdinQueue = StreamQueue(stdin); /// /// /// Returns an operation that completes when the user sends a line to /// /// standard input. @@ -365,7 +365,7 @@ class StreamQueue { CancelableOperation cancelable( Future callback(StreamQueue queue)) { var transaction = startTransaction(); - var completer = new CancelableCompleter(onCancel: () { + var completer = CancelableCompleter(onCancel: () { transaction.reject(); }); @@ -393,17 +393,17 @@ class StreamQueue { /// After calling `cancel`, no further events can be requested. /// None of [lookAhead], [next], [peek], [rest], [skip], [take] or [cancel] /// may be called again. - Future cancel({bool immediate: false}) { + Future cancel({bool immediate = false}) { if (_isClosed) throw _failClosed(); _isClosed = true; if (!immediate) { - var request = new _CancelRequest(this); + var request = _CancelRequest(this); _addRequest(request); return request.future; } - if (_isDone && _eventQueue.isEmpty) return new Future.value(); + if (_isDone && _eventQueue.isEmpty) return Future.value(); return _cancel(); } @@ -444,7 +444,7 @@ class StreamQueue { Stream _extractStream() { assert(_isClosed); if (_isDone) { - return new Stream.empty(); + return Stream.empty(); } _isDone = true; @@ -456,7 +456,7 @@ class StreamQueue { _subscription = null; var wasPaused = subscription.isPaused; - var result = new SubscriptionStream(subscription); + var result = SubscriptionStream(subscription); // Resume after creating stream because that pauses the subscription too. // This way there won't be a short resumption in the middle. if (wasPaused) subscription.resume(); @@ -481,9 +481,9 @@ class StreamQueue { if (_isDone) return; if (_subscription == null) { _subscription = _source.listen((data) { - _addResult(new Result.value(data)); + _addResult(Result.value(data)); }, onError: (error, StackTrace stackTrace) { - _addResult(new Result.error(error, stackTrace)); + _addResult(Result.error(error, stackTrace)); }, onDone: () { _subscription = null; this._close(); @@ -529,7 +529,7 @@ class StreamQueue { /// Returns a [StateError] with a message saying that either /// [cancel] or [rest] have already been called. Error _failClosed() { - return new StateError("Already cancelled"); + return StateError("Already cancelled"); } /// Adds a new request to the queue. @@ -558,7 +558,7 @@ class StreamQueueTransaction { final StreamSplitter _splitter; /// Queues created using [newQueue]. - final _queues = new Set(); + final _queues = Set(); /// Whether [commit] has been called. var _committed = false; @@ -567,7 +567,7 @@ class StreamQueueTransaction { var _rejected = false; StreamQueueTransaction._(this._parent, Stream source) - : _splitter = new StreamSplitter(source); + : _splitter = StreamSplitter(source); /// Creates a new copy of the parent queue. /// @@ -575,7 +575,7 @@ class StreamQueueTransaction { /// [StreamQueue.startTransaction] was called. Its position can be committed /// to the parent queue using [commit]. StreamQueue newQueue() { - var queue = new StreamQueue(_splitter.split()); + var queue = StreamQueue(_splitter.split()); _queues.add(queue); return queue; } @@ -592,9 +592,9 @@ class StreamQueueTransaction { void commit(StreamQueue queue) { _assertActive(); if (!_queues.contains(queue)) { - throw new ArgumentError("Queue doesn't belong to this transaction."); + throw ArgumentError("Queue doesn't belong to this transaction."); } else if (queue._requestQueue.isNotEmpty) { - throw new StateError("A queue with pending requests can't be committed."); + throw StateError("A queue with pending requests can't be committed."); } _committed = true; @@ -639,9 +639,9 @@ class StreamQueueTransaction { /// Throws a [StateError] if [accept] or [reject] has already been called. void _assertActive() { if (_committed) { - throw new StateError("This transaction has already been accepted."); + throw StateError("This transaction has already been accepted."); } else if (_rejected) { - throw new StateError("This transaction has already been rejected."); + throw StateError("This transaction has already been rejected."); } } } @@ -690,7 +690,7 @@ abstract class _EventRequest { /// and is then complete. class _NextRequest implements _EventRequest { /// Completer for the future returned by [StreamQueue.next]. - final _completer = new Completer(); + final _completer = Completer(); _NextRequest(); @@ -702,8 +702,7 @@ class _NextRequest implements _EventRequest { return true; } if (isDone) { - _completer.completeError( - new StateError("No elements"), StackTrace.current); + _completer.completeError(StateError("No elements"), StackTrace.current); return true; } return false; @@ -716,7 +715,7 @@ class _NextRequest implements _EventRequest { /// and is then complete, but doesn't consume the event. class _PeekRequest implements _EventRequest { /// Completer for the future returned by [StreamQueue.next]. - final _completer = new Completer(); + final _completer = Completer(); _PeekRequest(); @@ -728,8 +727,7 @@ class _PeekRequest implements _EventRequest { return true; } if (isDone) { - _completer.completeError( - new StateError("No elements"), StackTrace.current); + _completer.completeError(StateError("No elements"), StackTrace.current); return true; } return false; @@ -739,7 +737,7 @@ class _PeekRequest implements _EventRequest { /// Request for a [StreamQueue.skip] call. class _SkipRequest implements _EventRequest { /// Completer for the future returned by the skip call. - final _completer = new Completer(); + final _completer = Completer(); /// Number of remaining events to skip. /// @@ -776,7 +774,7 @@ class _SkipRequest implements _EventRequest { /// Common superclass for [_TakeRequest] and [_LookAheadRequest]. abstract class _ListRequest implements _EventRequest { /// Completer for the future returned by the take call. - final _completer = new Completer>(); + final _completer = Completer>(); /// List collecting events until enough have been seen. final _list = []; @@ -846,7 +844,7 @@ class _LookAheadRequest extends _ListRequest { class _CancelRequest implements _EventRequest { /// Completer for the future returned by the `cancel` call. /// TODO(lrn); make this Completer when that is implemented. - final _completer = new Completer(); + final _completer = Completer(); /// When the event is completed, it needs to cancel the active subscription /// of the `StreamQueue` object, if any. @@ -875,7 +873,7 @@ class _CancelRequest implements _EventRequest { /// stream events subscription and creates a stream from it. class _RestRequest implements _EventRequest { /// Completer for the stream returned by the `rest` call. - final _completer = new StreamCompleter(); + final _completer = StreamCompleter(); /// The [StreamQueue] object that has this request queued. /// @@ -898,7 +896,7 @@ class _RestRequest implements _EventRequest { } else { // There are prefetched events which needs to be added before the // remaining stream. - var controller = new StreamController(); + var controller = StreamController(); for (var event in events) { event.addTo(controller); } @@ -918,7 +916,7 @@ class _RestRequest implements _EventRequest { /// If the request is closed without seeing an event, then /// the [future] is completed with `false`. class _HasNextRequest implements _EventRequest { - final _completer = new Completer(); + final _completer = Completer(); Future get future => _completer.future; @@ -947,13 +945,13 @@ class _TransactionRequest implements _EventRequest { StreamQueueTransaction _transaction; /// The controller that passes events to [transaction]. - final _controller = new StreamController(sync: true); + final _controller = StreamController(sync: true); /// The number of events passed to [_controller] so far. var _eventsSent = 0; _TransactionRequest(StreamQueue parent) { - _transaction = new StreamQueueTransaction._(parent, _controller.stream); + _transaction = StreamQueueTransaction._(parent, _controller.stream); } bool update(QueueList> events, bool isDone) { diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index 42191268..c7fdd03d 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -24,7 +24,7 @@ class StreamSinkCompleter { /// /// Events can be added to the sink either before or after a destination sink /// is set. - final StreamSink sink = new _CompleterSink(); + final StreamSink sink = _CompleterSink(); /// Returns [sink] typed as a [_CompleterSink]. _CompleterSink get _sink => sink; @@ -37,7 +37,7 @@ class StreamSinkCompleter { /// If the future completes with an error, the returned sink will instead /// be closed. Its [Sink.done] future will contain the error. static StreamSink fromFuture(Future> sinkFuture) { - var completer = new StreamSinkCompleter(); + var completer = StreamSinkCompleter(); sinkFuture.then(completer.setDestinationSink, onError: completer.setError); return completer.sink; } @@ -59,7 +59,7 @@ class StreamSinkCompleter { /// Trying to call either of them again will fail. void setDestinationSink(StreamSink destinationSink) { if (_sink._destinationSink != null) { - throw new StateError("Destination sink already set"); + throw StateError("Destination sink already set"); } _sink._setDestinationSink(destinationSink); } @@ -71,7 +71,7 @@ class StreamSinkCompleter { /// Either of [setDestinationSink] or [setError] may be called at most once. /// Trying to call either of them again will fail. void setError(error, [StackTrace stackTrace]) { - setDestinationSink(new NullStreamSink.error(error, stackTrace)); + setDestinationSink(NullStreamSink.error(error, stackTrace)); } } @@ -101,7 +101,7 @@ class _CompleterSink implements StreamSink { Future get done { if (_doneCompleter != null) return _doneCompleter.future; if (_destinationSink == null) { - _doneCompleter = new Completer.sync(); + _doneCompleter = Completer.sync(); return _doneCompleter.future; } return _destinationSink.done; @@ -144,7 +144,7 @@ class _CompleterSink implements StreamSink { /// Create [_controller] if it doesn't yet exist. void _ensureController() { - if (_controller == null) _controller = new StreamController(sync: true); + if (_controller == null) _controller = StreamController(sync: true); } /// Sets the destination sink to which events from this sink will be provided. diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index 503d28a7..b01132ed 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -37,7 +37,7 @@ abstract class StreamSinkTransformer { {void handleData(S data, EventSink sink), void handleError(Object error, StackTrace stackTrace, EventSink sink), void handleDone(EventSink sink)}) { - return new HandlerTransformer(handleData, handleError, handleDone); + return HandlerTransformer(handleData, handleError, handleDone); } /// Transforms the events passed to [sink]. @@ -57,5 +57,5 @@ abstract class StreamSinkTransformer { StreamSinkTransformer transformer) => transformer is StreamSinkTransformer ? transformer - : new TypeSafeStreamSinkTransformer(transformer); + : TypeSafeStreamSinkTransformer(transformer); } diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index dba82408..31370035 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -8,14 +8,14 @@ import '../stream_sink_transformer.dart'; import '../delegate/stream_sink.dart'; /// The type of the callback for handling data events. -typedef void HandleData(S data, EventSink sink); +typedef HandleData = void Function(S data, EventSink sink); /// The type of the callback for handling error events. -typedef void HandleError( +typedef HandleError = void Function( Object error, StackTrace stackTrace, EventSink sink); /// The type of the callback for handling done events. -typedef void HandleDone(EventSink sink); +typedef HandleDone = void Function(EventSink sink); /// A [StreamSinkTransformer] that delegates events to the given handlers. class HandlerTransformer implements StreamSinkTransformer { @@ -30,7 +30,7 @@ class HandlerTransformer implements StreamSinkTransformer { HandlerTransformer(this._handleData, this._handleError, this._handleDone); - StreamSink bind(StreamSink sink) => new _HandlerSink(this, sink); + StreamSink bind(StreamSink sink) => _HandlerSink(this, sink); } /// A sink created by [HandlerTransformer]. @@ -49,7 +49,7 @@ class _HandlerSink implements StreamSink { _HandlerSink(this._transformer, StreamSink inner) : _inner = inner, - _safeCloseInner = new _SafeCloseSink(inner); + _safeCloseInner = _SafeCloseSink(inner); void add(S event) { if (_transformer._handleData == null) { @@ -69,7 +69,7 @@ class _HandlerSink implements StreamSink { Future addStream(Stream stream) { return _inner.addStream(stream.transform( - new StreamTransformer.fromHandlers( + StreamTransformer.fromHandlers( handleData: _transformer._handleData, handleError: _transformer._handleError, handleDone: _closeSink))); diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart index 32ac648d..1010f9fe 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -14,7 +14,7 @@ class StreamTransformerWrapper implements StreamSinkTransformer { const StreamTransformerWrapper(this._transformer); StreamSink bind(StreamSink sink) => - new _StreamTransformerWrapperSink(_transformer, sink); + _StreamTransformerWrapperSink(_transformer, sink); } /// A sink created by [StreamTransformerWrapper]. @@ -23,7 +23,7 @@ class _StreamTransformerWrapperSink implements StreamSink { /// /// This is used to create a stream that can be transformed by the wrapped /// transformer. - final _controller = new StreamController(sync: true); + final _controller = StreamController(sync: true); /// The original sink that's being transformed. final StreamSink _inner; diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index 3616501c..9ddb63a6 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -32,20 +32,20 @@ class StreamSplitter { /// The buffer of events or errors that have already been emitted by /// [_stream]. - final _buffer = new List>(); + final _buffer = List>(); /// The controllers for branches that are listening for future events from /// [_stream]. /// /// Once a branch is canceled, it's removed from this list. When [_stream] is /// done, all branches are removed. - final _controllers = new Set>(); + final _controllers = Set>(); /// A group of futures returned by [close]. /// /// This is used to ensure that [close] doesn't complete until all /// [StreamController.close] and [StreamSubscription.cancel] calls complete. - final _closeGroup = new FutureGroup(); + final _closeGroup = FutureGroup(); /// Whether [_stream] is done emitting events. var _isDone = false; @@ -59,8 +59,8 @@ class StreamSplitter { /// then closing the [StreamSplitter]. static List> splitFrom(Stream stream, [int count]) { if (count == null) count = 2; - var splitter = new StreamSplitter(stream); - var streams = new List>.generate(count, (_) => splitter.split()); + var splitter = StreamSplitter(stream); + var streams = List>.generate(count, (_) => splitter.split()); splitter.close(); return streams; } @@ -72,10 +72,10 @@ class StreamSplitter { /// This will throw a [StateError] if [close] has been called. Stream split() { if (_isClosed) { - throw new StateError("Can't call split() on a closed StreamSplitter."); + throw StateError("Can't call split() on a closed StreamSplitter."); } - var controller = new StreamController( + var controller = StreamController( onListen: _onListen, onPause: _onPause, onResume: _onResume); controller.onCancel = () => _onCancel(controller); @@ -183,7 +183,7 @@ class StreamSplitter { /// Buffers [data] and passes it to [_controllers]. void _onData(T data) { - if (!_isClosed) _buffer.add(new Result.value(data)); + if (!_isClosed) _buffer.add(Result.value(data)); for (var controller in _controllers) { controller.add(data); } @@ -191,7 +191,7 @@ class StreamSplitter { /// Buffers [error] and passes it to [_controllers]. void _onError(Object error, StackTrace stackTrace) { - if (!_isClosed) _buffer.add(new Result.error(error, stackTrace)); + if (!_isClosed) _buffer.add(Result.error(error, stackTrace)); for (var controller in _controllers) { controller.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart index 1443b183..bee96a50 100644 --- a/pkgs/async/lib/src/stream_subscription_transformer.dart +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -6,9 +6,9 @@ import 'dart:async'; import 'async_memoizer.dart'; -typedef Future _AsyncHandler(StreamSubscription inner); +typedef _AsyncHandler = Future Function(StreamSubscription inner); -typedef void _VoidHandler(StreamSubscription inner); +typedef _VoidHandler = void Function(StreamSubscription inner); /// Creates a [StreamTransformer] that modifies the behavior of subscriptions to /// a stream. @@ -31,8 +31,8 @@ StreamTransformer subscriptionTransformer( {Future handleCancel(StreamSubscription inner), void handlePause(StreamSubscription inner), void handleResume(StreamSubscription inner)}) { - return new StreamTransformer((stream, cancelOnError) { - return new _TransformedSubscription( + return StreamTransformer((stream, cancelOnError) { + return _TransformedSubscription( stream.listen(null, cancelOnError: cancelOnError), handleCancel ?? (inner) => inner.cancel(), handlePause ?? @@ -88,7 +88,7 @@ class _TransformedSubscription implements StreamSubscription { _inner = null; return _handleCancel(inner); }); - final _cancelMemoizer = new AsyncMemoizer(); + final _cancelMemoizer = AsyncMemoizer(); void pause([Future resumeFuture]) { if (_cancelMemoizer.hasRun) return; @@ -102,5 +102,5 @@ class _TransformedSubscription implements StreamSubscription { } Future asFuture([E futureValue]) => - _inner?.asFuture(futureValue) ?? new Completer().future; + _inner?.asFuture(futureValue) ?? Completer().future; } diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index 3d5a8113..ef14c3eb 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -31,7 +31,7 @@ class StreamZip extends Stream> { dataCount++; if (dataCount == subscriptions.length) { var data = current; - current = new List(subscriptions.length); + current = List(subscriptions.length); dataCount = 0; for (int i = 0; i < subscriptions.length; i++) { if (i != index) subscriptions[i].resume(); @@ -84,9 +84,9 @@ class StreamZip extends Stream> { rethrow; } - current = new List(subscriptions.length); + current = List(subscriptions.length); - controller = new StreamController>(onPause: () { + controller = StreamController>(onPause: () { for (int i = 0; i < subscriptions.length; i++) { // This may pause some subscriptions more than once. // These will not be resumed by onResume below, but must wait for the diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index a2356631..347c3469 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -41,14 +41,14 @@ class SubscriptionStream extends Stream { StreamSubscription listen(void onData(T event), {Function onError, void onDone(), bool cancelOnError}) { if (_source == null) { - throw new StateError("Stream has already been listened to."); + throw StateError("Stream has already been listened to."); } cancelOnError = (true == cancelOnError); var subscription = _source; _source = null; var result = cancelOnError - ? new _CancelOnErrorSubscriptionWrapper(subscription) + ? _CancelOnErrorSubscriptionWrapper(subscription) : subscription; result.onData(onData); result.onError(onError); diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart index cb63311a..9750eb42 100644 --- a/pkgs/async/lib/src/typed_stream_transformer.dart +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -16,7 +16,7 @@ StreamTransformer typedStreamTransformer( StreamTransformer transformer) => transformer is StreamTransformer ? transformer - : new _TypeSafeStreamTransformer(transformer); + : _TypeSafeStreamTransformer(transformer); /// A wrapper that coerces the type of the stream returned by an inner /// transformer. diff --git a/pkgs/async/lib/src/utils.dart b/pkgs/async/lib/src/utils.dart index 2aab40e1..39e5a8a8 100644 --- a/pkgs/async/lib/src/utils.dart +++ b/pkgs/async/lib/src/utils.dart @@ -5,8 +5,8 @@ import 'dart:async'; /// A generic typedef for a function that takes one type and returns another. -typedef F UnaryFunction(E argument); +typedef UnaryFunction = F Function(E argument); /// A typedef for a function that takes no arguments and returns a Future or a /// value. -typedef FutureOr FutureOrCallback(); +typedef FutureOrCallback = FutureOr Function(); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 37db6601..0b791785 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -6,7 +6,7 @@ author: Dart Team homepage: https://www.github.com/dart-lang/async environment: - sdk: '>=2.0.0-dev.23.0 <3.0.0' + sdk: '>=2.0.0 <3.0.0' dependencies: collection: ^1.5.0 diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index a4f452f6..92e34bfa 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -13,7 +13,7 @@ void main() { setUp(() { // Create a cache that is fresh for an hour. - cache = new AsyncCache(const Duration(hours: 1)); + cache = AsyncCache(const Duration(hours: 1)); }); test('should fetch via a callback when no cache exists', () async { @@ -27,16 +27,16 @@ void main() { test('should not fetch via callback when a future is in-flight', () async { // No actual caching is done, just avoid duplicate requests. - cache = new AsyncCache.ephemeral(); + cache = AsyncCache.ephemeral(); - var completer = new Completer(); + var completer = Completer(); expect(cache.fetch(() => completer.future), completion('Expensive')); expect(cache.fetch(expectAsync0(() {}, count: 0)), completion('Expensive')); completer.complete('Expensive'); }); test('should fetch via a callback again when cache expires', () { - new FakeAsync().run((fakeAsync) async { + FakeAsync().run((fakeAsync) async { var timesCalled = 0; call() async => 'Called ${++timesCalled}'; expect(await cache.fetch(call), 'Called 1'); @@ -67,7 +67,7 @@ void main() { test('should fetch a stream via a callback', () async { expect( await cache.fetchStream(expectAsync0(() { - return new Stream.fromIterable(['1', '2', '3']); + return Stream.fromIterable(['1', '2', '3']); })).toList(), ['1', '2', '3']); }); @@ -86,22 +86,22 @@ void main() { // Unlike the above test, we want to verify that we don't make multiple // calls if a cache is being filled currently, and instead wait for that // cache to be completed. - var controller = new StreamController(); + var controller = StreamController(); Stream call() => controller.stream; expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); controller.add('1'); controller.add('2'); - await new Future.value(); + await Future.value(); expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); controller.add('3'); await controller.close(); }); test('should fetch stream via a callback again when cache expires', () { - new FakeAsync().run((fakeAsync) async { + FakeAsync().run((fakeAsync) async { var timesCalled = 0; Stream call() { - return new Stream.fromIterable(['Called ${++timesCalled}']); + return Stream.fromIterable(['Called ${++timesCalled}']); } expect(await cache.fetchStream(call).toList(), ['Called 1']); @@ -125,7 +125,7 @@ void main() { test('should fetch via a callback when manually invalidated', () async { var timesCalled = 0; Stream call() { - return new Stream.fromIterable(['Called ${++timesCalled}']); + return Stream.fromIterable(['Called ${++timesCalled}']); } expect(await cache.fetchStream(call).toList(), ['Called 1']); @@ -136,7 +136,7 @@ void main() { }); test('should cancel a cached stream without affecting others', () async { - Stream call() => new Stream.fromIterable(['1', '2', '3']); + Stream call() => Stream.fromIterable(['1', '2', '3']); expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); @@ -145,7 +145,7 @@ void main() { }); test('should pause a cached stream without affecting others', () async { - Stream call() => new Stream.fromIterable(['1', '2', '3']); + Stream call() => Stream.fromIterable(['1', '2', '3']); StreamSubscription sub; sub = cache.fetchStream(call).listen(expectAsync1((event) { diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart index 380f58dc..785f9a55 100644 --- a/pkgs/async/test/async_memoizer_test.dart +++ b/pkgs/async/test/async_memoizer_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; main() { AsyncMemoizer cache; - setUp(() => cache = new AsyncMemoizer()); + setUp(() => cache = AsyncMemoizer()); test("runs the function only the first time runOnce() is called", () async { var count = 0; diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart index b87b0741..24d218b1 100644 --- a/pkgs/async/test/byte_collection_test.dart +++ b/pkgs/async/test/byte_collection_test.dart @@ -10,7 +10,7 @@ import "package:async/async.dart"; void main() { group("collectBytes", () { test("simple list and overflow", () { - var result = collectBytes(new Stream.fromIterable([ + var result = collectBytes(Stream.fromIterable([ [0], [1], [2], @@ -20,25 +20,25 @@ void main() { }); test("no events", () { - var result = collectBytes(new Stream.fromIterable([])); + var result = collectBytes(Stream.fromIterable([])); expect(result, completion([])); }); test("empty events", () { - var result = collectBytes(new Stream.fromIterable([[], []])); + var result = collectBytes(Stream.fromIterable([[], []])); expect(result, completion([])); }); test("error event", () { - var result = collectBytes(new Stream.fromIterable( - new Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); + var result = collectBytes(Stream.fromIterable( + Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); expect(result, throwsA("badness")); }); }); group("collectBytes", () { test("simple list and overflow", () { - var result = collectBytesCancelable(new Stream.fromIterable([ + var result = collectBytesCancelable(Stream.fromIterable([ [0], [1], [2], @@ -48,23 +48,23 @@ void main() { }); test("no events", () { - var result = collectBytesCancelable(new Stream.fromIterable([])); + var result = collectBytesCancelable(Stream.fromIterable([])); expect(result.value, completion([])); }); test("empty events", () { - var result = collectBytesCancelable(new Stream.fromIterable([[], []])); + var result = collectBytesCancelable(Stream.fromIterable([[], []])); expect(result.value, completion([])); }); test("error event", () { - var result = collectBytesCancelable(new Stream.fromIterable( - new Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); + var result = collectBytesCancelable(Stream.fromIterable( + Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); expect(result.value, throwsA("badness")); }); test("cancelled", () async { - var sc = new StreamController>(); + var sc = StreamController>(); var result = collectBytesCancelable(sc.stream); // Value never completes. result.value.whenComplete(expectAsync0(() {}, count: 0)); @@ -87,4 +87,4 @@ void main() { }); } -Future nextTimerTick() => new Future(() {}); +Future nextTimerTick() => Future(() {}); diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 8c92e2a2..4a238561 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -13,8 +13,7 @@ void main() { group("without being canceled", () { CancelableCompleter completer; setUp(() { - completer = - new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); + completer = CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); }); test("sends values to the future", () { @@ -34,14 +33,14 @@ void main() { test("sends values in a future to the future", () { expect(completer.operation.value, completion(equals(1))); expect(completer.isCompleted, isFalse); - completer.complete(new Future.value(1)); + completer.complete(Future.value(1)); expect(completer.isCompleted, isTrue); }); test("sends errors in a future to the future", () { expect(completer.operation.value, throwsA("error")); expect(completer.isCompleted, isFalse); - completer.complete(new Future.error("error")); + completer.complete(Future.error("error")); expect(completer.isCompleted, isTrue); }); @@ -74,31 +73,28 @@ void main() { test("successfully then with a future", () { completer.complete(1); - expect( - () => completer.complete(new Completer().future), throwsStateError); + expect(() => completer.complete(Completer().future), throwsStateError); }); test("with a future then successfully", () { - completer.complete(new Completer().future); + completer.complete(Completer().future); expect(() => completer.complete(1), throwsStateError); }); test("with a future twice", () { - completer.complete(new Completer().future); - expect( - () => completer.complete(new Completer().future), throwsStateError); + completer.complete(Completer().future); + expect(() => completer.complete(Completer().future), throwsStateError); }); }); group("CancelableOperation.fromFuture", () { test("forwards values", () { - var operation = new CancelableOperation.fromFuture(new Future.value(1)); + var operation = CancelableOperation.fromFuture(Future.value(1)); expect(operation.value, completion(equals(1))); }); test("forwards errors", () { - var operation = - new CancelableOperation.fromFuture(new Future.error("error")); + var operation = CancelableOperation.fromFuture(Future.error("error")); expect(operation.value, throwsA("error")); }); }); @@ -106,7 +102,7 @@ void main() { group("when canceled", () { test("causes the future never to fire", () async { - var completer = new CancelableCompleter(); + var completer = CancelableCompleter(); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); completer.operation.cancel(); @@ -119,7 +115,7 @@ void main() { test("fires onCancel", () { var canceled = false; CancelableCompleter completer; - completer = new CancelableCompleter(onCancel: expectAsync0(() { + completer = CancelableCompleter(onCancel: expectAsync0(() { expect(completer.isCanceled, isTrue); canceled = true; })); @@ -134,8 +130,8 @@ void main() { }); test("returns the onCancel future each time cancel is called", () { - var completer = new CancelableCompleter(onCancel: expectAsync0(() { - return new Future.value(1); + var completer = CancelableCompleter(onCancel: expectAsync0(() { + return Future.value(1); })); expect(completer.operation.cancel(), completion(equals(1))); expect(completer.operation.cancel(), completion(equals(1))); @@ -143,13 +139,13 @@ void main() { }); test("returns a future even if onCancel doesn't", () { - var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); + var completer = CancelableCompleter(onCancel: expectAsync0(() {})); expect(completer.operation.cancel(), completes); }); test("doesn't call onCancel if the completer has completed", () { var completer = - new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); + CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); completer.complete(1); expect(completer.operation.value, completion(equals(1))); expect(completer.operation.cancel(), completes); @@ -158,8 +154,8 @@ void main() { test( "does call onCancel if the completer has completed to an unfired " "Future", () { - var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); - completer.complete(new Completer().future); + var completer = CancelableCompleter(onCancel: expectAsync0(() {})); + completer.complete(Completer().future); expect(completer.operation.cancel(), completes); }); @@ -167,14 +163,14 @@ void main() { "doesn't call onCancel if the completer has completed to a fired " "Future", () async { var completer = - new CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); - completer.complete(new Future.value(1)); + CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); + completer.complete(Future.value(1)); await completer.operation.value; expect(completer.operation.cancel(), completes); }); test("can be completed once after being canceled", () async { - var completer = new CancelableCompleter(); + var completer = CancelableCompleter(); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); await completer.operation.cancel(); completer.complete(1); @@ -182,13 +178,13 @@ void main() { }); test("fires valueOrCancellation with the given value", () { - var completer = new CancelableCompleter(); + var completer = CancelableCompleter(); expect(completer.operation.valueOrCancellation(1), completion(equals(1))); completer.operation.cancel(); }); test("pipes an error through valueOrCancellation", () { - var completer = new CancelableCompleter(onCancel: () { + var completer = CancelableCompleter(onCancel: () { throw "error"; }); expect(completer.operation.valueOrCancellation(1), throwsA("error")); @@ -196,9 +192,9 @@ void main() { }); test("valueOrCancellation waits on the onCancel future", () async { - var innerCompleter = new Completer(); + var innerCompleter = Completer(); var completer = - new CancelableCompleter(onCancel: () => innerCompleter.future); + CancelableCompleter(onCancel: () => innerCompleter.future); var fired = false; completer.operation.valueOrCancellation().then((_) { @@ -217,21 +213,21 @@ void main() { group("asStream()", () { test("emits a value and then closes", () { - var completer = new CancelableCompleter(); + var completer = CancelableCompleter(); expect(completer.operation.asStream().toList(), completion(equals([1]))); completer.complete(1); }); test("emits an error and then closes", () { - var completer = new CancelableCompleter(); - var queue = new StreamQueue(completer.operation.asStream()); + var completer = CancelableCompleter(); + var queue = StreamQueue(completer.operation.asStream()); expect(queue.next, throwsA("error")); expect(queue.hasNext, completion(isFalse)); completer.completeError("error"); }); test("cancels the completer when the subscription is canceled", () { - var completer = new CancelableCompleter(onCancel: expectAsync0(() {})); + var completer = CancelableCompleter(onCancel: expectAsync0(() {})); var sub = completer.operation.asStream().listen(expectAsync1((_) {}, count: 0)); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index b6f75ba3..770a465b 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { FutureGroup futureGroup; setUp(() { - futureGroup = new FutureGroup(); + futureGroup = FutureGroup(); }); group("with no futures", () { @@ -32,7 +32,7 @@ void main() { group("with a future that already completed", () { test("never completes if nothing happens", () async { - futureGroup.add(new Future.value()); + futureGroup.add(Future.value()); await flushMicrotasks(); var completed = false; @@ -43,7 +43,7 @@ void main() { }); test("completes once it's closed", () async { - futureGroup.add(new Future.value()); + futureGroup.add(Future.value()); await flushMicrotasks(); expect(futureGroup.future, completes); @@ -51,21 +51,21 @@ void main() { }); test("completes to that future's value", () { - futureGroup.add(new Future.value(1)); + futureGroup.add(Future.value(1)); futureGroup.close(); expect(futureGroup.future, completion(equals([1]))); }); test("completes to that future's error, even if it's not closed", () { - futureGroup.add(new Future.error("error")); + futureGroup.add(Future.error("error")); expect(futureGroup.future, throwsA("error")); }); }); test("completes once all contained futures complete", () async { - var completer1 = new Completer(); - var completer2 = new Completer(); - var completer3 = new Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -89,9 +89,9 @@ void main() { }); test("completes to the values of the futures in order of addition", () { - var completer1 = new Completer(); - var completer2 = new Completer(); - var completer3 = new Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -108,9 +108,9 @@ void main() { test("completes to the first error to be emitted, even if it's not closed", () { - var completer1 = new Completer(); - var completer2 = new Completer(); - var completer3 = new Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -126,9 +126,9 @@ void main() { var idle = false; futureGroup.onIdle.listen((_) => idle = true); - var completer1 = new Completer(); - var completer2 = new Completer(); - var completer3 = new Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -158,7 +158,7 @@ void main() { var idle = false; futureGroup.onIdle.listen((_) => idle = true); - var completer = new Completer(); + var completer = Completer(); futureGroup.add(completer.future); completer.complete(); @@ -167,7 +167,7 @@ void main() { expect(futureGroup.isIdle, isTrue); idle = false; - completer = new Completer(); + completer = Completer(); futureGroup.add(completer.future); await flushMicrotasks(); @@ -202,7 +202,7 @@ void main() { futureFired = true; })); - var completer = new Completer(); + var completer = Completer(); futureGroup.add(completer.future); futureGroup.close(); diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index 0c2e3441..db1bc94e 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -11,14 +11,14 @@ import "utils.dart"; main() { test("disallows a null callback", () { - expect(() => new LazyStream(null), throwsArgumentError); + expect(() => LazyStream(null), throwsArgumentError); }); test("calls the callback when the stream is listened", () async { var callbackCalled = false; - var stream = new LazyStream(expectAsync0(() { + var stream = LazyStream(expectAsync0(() { callbackCalled = true; - return new Stream.empty(); + return Stream.empty(); })); await flushMicrotasks(); @@ -30,9 +30,9 @@ main() { test("calls the callback when the stream is listened", () async { var callbackCalled = false; - var stream = new LazyStream(expectAsync0(() { + var stream = LazyStream(expectAsync0(() { callbackCalled = true; - return new Stream.empty(); + return Stream.empty(); })); await flushMicrotasks(); @@ -43,8 +43,8 @@ main() { }); test("forwards to a synchronously-provided stream", () async { - var controller = new StreamController(); - var stream = new LazyStream(expectAsync0(() => controller.stream)); + var controller = StreamController(); + var stream = LazyStream(expectAsync0(() => controller.stream)); var events = []; stream.listen(events.add); @@ -65,8 +65,8 @@ main() { }); test("forwards to an asynchronously-provided stream", () async { - var controller = new StreamController(); - var stream = new LazyStream(expectAsync0(() async => controller.stream)); + var controller = StreamController(); + var stream = LazyStream(expectAsync0(() async => controller.stream)); var events = []; stream.listen(events.add); @@ -87,7 +87,7 @@ main() { }); test("a lazy stream can't be listened to multiple times", () { - var stream = new LazyStream(expectAsync0(() => new Stream.empty())); + var stream = LazyStream(expectAsync0(() => Stream.empty())); expect(stream.isBroadcast, isFalse); stream.listen(null); @@ -97,9 +97,9 @@ main() { test("a lazy stream can't be listened to from within its callback", () { LazyStream stream; - stream = new LazyStream(expectAsync0(() { + stream = LazyStream(expectAsync0(() { expect(() => stream.listen(null), throwsStateError); - return new Stream.empty(); + return Stream.empty(); })); stream.listen(null); }); diff --git a/pkgs/async/test/null_stream_sink_test.dart b/pkgs/async/test/null_stream_sink_test.dart index 244f99c6..33e2655f 100644 --- a/pkgs/async/test/null_stream_sink_test.dart +++ b/pkgs/async/test/null_stream_sink_test.dart @@ -12,13 +12,13 @@ import "utils.dart"; void main() { group("constructors", () { test("done defaults to a completed future", () { - var sink = new NullStreamSink(); + var sink = NullStreamSink(); expect(sink.done, completes); }); test("a custom future may be passed to done", () async { - var completer = new Completer(); - var sink = new NullStreamSink(done: completer.future); + var completer = Completer(); + var sink = NullStreamSink(done: completer.future); var doneFired = false; sink.done.then((_) { @@ -33,32 +33,32 @@ void main() { }); test("NullStreamSink.error passes an error to done", () { - var sink = new NullStreamSink.error("oh no"); + var sink = NullStreamSink.error("oh no"); expect(sink.done, throwsA("oh no")); }); }); group("events", () { test("are silently dropped before close", () { - var sink = new NullStreamSink(); + var sink = NullStreamSink(); sink.add(1); sink.addError("oh no"); }); test("throw StateErrors after close", () { - var sink = new NullStreamSink(); + var sink = NullStreamSink(); expect(sink.close(), completes); expect(() => sink.add(1), throwsStateError); expect(() => sink.addError("oh no"), throwsStateError); - expect(() => sink.addStream(new Stream.empty()), throwsStateError); + expect(() => sink.addStream(Stream.empty()), throwsStateError); }); group("addStream", () { test("listens to the stream then cancels immediately", () async { - var sink = new NullStreamSink(); + var sink = NullStreamSink(); var canceled = false; - var controller = new StreamController(onCancel: () { + var controller = StreamController(onCancel: () { canceled = true; }); @@ -68,9 +68,9 @@ void main() { }); test("returns the cancel future", () async { - var completer = new Completer(); - var sink = new NullStreamSink(); - var controller = new StreamController(onCancel: () => completer.future); + var completer = Completer(); + var sink = NullStreamSink(); + var controller = StreamController(onCancel: () => completer.future); var addStreamFired = false; sink.addStream(controller.stream).then((_) { @@ -85,29 +85,29 @@ void main() { }); test("pipes errors from the cancel future through addStream", () async { - var sink = new NullStreamSink(); - var controller = new StreamController(onCancel: () => throw "oh no"); + var sink = NullStreamSink(); + var controller = StreamController(onCancel: () => throw "oh no"); expect(sink.addStream(controller.stream), throwsA("oh no")); }); test("causes events to throw StateErrors until the future completes", () async { - var sink = new NullStreamSink(); - var future = sink.addStream(new Stream.empty()); + var sink = NullStreamSink(); + var future = sink.addStream(Stream.empty()); expect(() => sink.add(1), throwsStateError); expect(() => sink.addError("oh no"), throwsStateError); - expect(() => sink.addStream(new Stream.empty()), throwsStateError); + expect(() => sink.addStream(Stream.empty()), throwsStateError); await future; sink.add(1); sink.addError("oh no"); - expect(sink.addStream(new Stream.empty()), completes); + expect(sink.addStream(Stream.empty()), completes); }); }); }); test("close returns the done future", () { - var sink = new NullStreamSink.error("oh no"); + var sink = NullStreamSink.error("oh no"); expect(sink.close(), throwsA("oh no")); }); } diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index 4f59f62c..c46c87b6 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -8,100 +8,99 @@ import 'package:test/test.dart'; main() { test("runs the callback once the duration has elapsed", () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var fired = false; - new RestartableTimer(new Duration(seconds: 5), () { + RestartableTimer(Duration(seconds: 5), () { fired = true; }); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); - async.elapse(new Duration(seconds: 1)); + async.elapse(Duration(seconds: 1)); expect(fired, isTrue); }); }); test("doesn't run the callback if the timer is canceled", () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var fired = false; - var timer = new RestartableTimer(new Duration(seconds: 5), () { + var timer = RestartableTimer(Duration(seconds: 5), () { fired = true; }); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); timer.cancel(); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); }); }); test("resets the duration if the timer is reset before it fires", () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var fired = false; - var timer = new RestartableTimer(new Duration(seconds: 5), () { + var timer = RestartableTimer(Duration(seconds: 5), () { fired = true; }); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); timer.reset(); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); - async.elapse(new Duration(seconds: 1)); + async.elapse(Duration(seconds: 1)); expect(fired, isTrue); }); }); test("re-runs the callback if the timer is reset after firing", () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var fired = 0; - var timer = new RestartableTimer(new Duration(seconds: 5), () { + var timer = RestartableTimer(Duration(seconds: 5), () { fired++; }); - async.elapse(new Duration(seconds: 5)); + async.elapse(Duration(seconds: 5)); expect(fired, equals(1)); timer.reset(); - async.elapse(new Duration(seconds: 5)); + async.elapse(Duration(seconds: 5)); expect(fired, equals(2)); timer.reset(); - async.elapse(new Duration(seconds: 5)); + async.elapse(Duration(seconds: 5)); expect(fired, equals(3)); }); }); test("runs the callback if the timer is reset after being canceled", () { - new FakeAsync().run((async) { + FakeAsync().run((async) { var fired = false; - var timer = new RestartableTimer(new Duration(seconds: 5), () { + var timer = RestartableTimer(Duration(seconds: 5), () { fired = true; }); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); timer.cancel(); - async.elapse(new Duration(seconds: 4)); + async.elapse(Duration(seconds: 4)); expect(fired, isFalse); timer.reset(); - async.elapse(new Duration(seconds: 5)); + async.elapse(Duration(seconds: 5)); expect(fired, isTrue); }); }); test("only runs the callback once if the timer isn't reset", () { - new FakeAsync().run((async) { - new RestartableTimer( - new Duration(seconds: 5), expectAsync0(() {}, count: 1)); - async.elapse(new Duration(seconds: 10)); + FakeAsync().run((async) { + RestartableTimer(Duration(seconds: 5), expectAsync0(() {}, count: 1)); + async.elapse(Duration(seconds: 10)); }); }); } diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index 172e38a9..c7b688f3 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -8,16 +8,16 @@ import "package:async/async.dart"; import "package:test/test.dart"; final someStack = StackTrace.current; -Result res(int n) => new Result.value(n); -Result err(n) => new ErrorResult("$n", someStack); +Result res(int n) => Result.value(n); +Result err(n) => ErrorResult("$n", someStack); /// Helper function creating an iterable of futures. Iterable> futures(int count, {bool throwWhen(int index)}) sync* { for (int i = 0; i < count; i++) { if (throwWhen != null && throwWhen(i)) { - yield new Future.error("$i", someStack); + yield Future.error("$i", someStack); } else { - yield new Future.value(i); + yield Future.value(i); } } } @@ -58,7 +58,7 @@ main() { }); test("completion permutation 1-2-3", () async { - var cs = new List.generate(3, (_) => new Completer()); + var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; @@ -70,7 +70,7 @@ main() { }); test("completion permutation 1-3-2", () async { - var cs = new List.generate(3, (_) => new Completer()); + var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; @@ -82,7 +82,7 @@ main() { }); test("completion permutation 2-1-3", () async { - var cs = new List.generate(3, (_) => new Completer()); + var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; @@ -94,7 +94,7 @@ main() { }); test("completion permutation 2-3-1", () async { - var cs = new List.generate(3, (_) => new Completer()); + var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; @@ -106,7 +106,7 @@ main() { }); test("completion permutation 3-1-2", () async { - var cs = new List.generate(3, (_) => new Completer()); + var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; @@ -118,7 +118,7 @@ main() { }); test("completion permutation 3-2-1", () async { - var cs = new List.generate(3, (_) => new Completer()); + var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; @@ -129,19 +129,19 @@ main() { cs[0].complete(1); }); - var seed = new Random().nextInt(0x100000000); + var seed = Random().nextInt(0x100000000); int n = 25; // max 32, otherwise rnd.nextInt(1< new Completer()); + var cs = List.generate(n, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); - var rnd = new Random(seed); + var rnd = Random(seed); var throwFlags = rnd.nextInt(1 << n); // Bit-flag for throwing. bool throws(index) => (throwFlags & (1 << index)) != 0; - var expected = new List.generate(n, (x) => throws(x) ? err(x) : res(x)); + var expected = List.generate(n, (x) => throws(x) ? err(x) : res(x)); expect(all, completion(expected)); - var completeFunctions = new List.generate(n, (i) { + var completeFunctions = List.generate(n, (i) { var c = cs[i]; return () => throws(i) ? c.completeError("$i", someStack) : c.complete(i); @@ -167,19 +167,19 @@ main() { test("no error", () async { var all = await Result.captureAll(>[ 1, - new Future(() => 2), + Future(() => 2), 3, - new Future.value(4), + Future.value(4), ]); expect(all, [res(1), res(2), res(3), res(4)]); }); test("error", () async { var all = await Result.captureAll(>[ 1, - new Future(() => 2), + Future(() => 2), 3, - new Future(() async => await new Future.error("4", someStack)), - new Future.value(5) + Future(() async => await Future.error("4", someStack)), + Future.value(5) ]); expect(all, [res(1), res(2), res(3), err(4), res(5)]); }); diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart index caaf668e..4a049eba 100644 --- a/pkgs/async/test/result/result_flattenAll_test.dart +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -6,8 +6,8 @@ import "package:async/async.dart"; import "package:test/test.dart"; final someStack = StackTrace.current; -Result res(T n) => new Result.value(n); -Result err(n) => new ErrorResult("$n", someStack); +Result res(T n) => Result.value(n); +Result err(n) => ErrorResult("$n", someStack); /// Helper function creating an iterable of results. Iterable> results(int count, {bool throwWhen(int index)}) sync* { diff --git a/pkgs/async/test/result/result_future_test.dart b/pkgs/async/test/result/result_future_test.dart index ffdbb7cf..4218db2b 100644 --- a/pkgs/async/test/result/result_future_test.dart +++ b/pkgs/async/test/result/result_future_test.dart @@ -12,8 +12,8 @@ void main() { Completer completer; ResultFuture future; setUp(() { - completer = new Completer(); - future = new ResultFuture(completer.future); + completer = Completer(); + future = ResultFuture(completer.future); }); test('before completion, result is null', () { @@ -30,7 +30,7 @@ void main() { }); test("after an error completion, result is the future's error", () { - var trace = new Trace.current(); + var trace = Trace.current(); completer.completeError('error', trace); // The completer calls its listeners asynchronously. We have to wait diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index 33e146e6..a33c1ba2 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -10,10 +10,10 @@ import "package:stack_trace/stack_trace.dart"; import "package:test/test.dart"; void main() { - var stack = new Trace.current(); + var stack = Trace.current(); test("create result value", () { - Result result = new Result.value(42); + Result result = Result.value(42); expect(result.isValue, isTrue); expect(result.isError, isFalse); ValueResult value = result.asValue; @@ -21,7 +21,7 @@ void main() { }); test("create result value 2", () { - Result result = new ValueResult(42); + Result result = ValueResult(42); expect(result.isValue, isTrue); expect(result.isError, isFalse); ValueResult value = result.asValue; @@ -29,7 +29,7 @@ void main() { }); test("create result error", () { - Result result = new Result.error("BAD", stack); + Result result = Result.error("BAD", stack); expect(result.isValue, isFalse); expect(result.isError, isTrue); ErrorResult error = result.asError; @@ -38,7 +38,7 @@ void main() { }); test("create result error 2", () { - Result result = new ErrorResult("BAD", stack); + Result result = ErrorResult("BAD", stack); expect(result.isValue, isFalse); expect(result.isError, isTrue); ErrorResult error = result.asError; @@ -47,7 +47,7 @@ void main() { }); test("create result error no stack", () { - Result result = new Result.error("BAD"); + Result result = Result.error("BAD"); expect(result.isValue, isFalse); expect(result.isError, isTrue); ErrorResult error = result.asError; @@ -56,8 +56,8 @@ void main() { }); test("complete with value", () { - Result result = new ValueResult(42); - var c = new Completer(); + Result result = ValueResult(42); + var c = Completer(); c.future.then(expectAsync1((int v) { expect(v, equals(42)); }), onError: (e, s) { @@ -67,8 +67,8 @@ void main() { }); test("complete with error", () { - Result result = new ErrorResult("BAD", stack); - var c = new Completer(); + Result result = ErrorResult("BAD", stack); + var c = Completer(); c.future.then((bool v) { fail("Unexpected value $v"); }, onError: expectAsync2((e, s) { @@ -79,16 +79,16 @@ void main() { }); test("add sink value", () { - var result = new ValueResult(42); - EventSink sink = new TestSink(onData: expectAsync1((v) { + var result = ValueResult(42); + EventSink sink = TestSink(onData: expectAsync1((v) { expect(v, equals(42)); })); result.addTo(sink); }); test("add sink error", () { - Result result = new ErrorResult("BAD", stack); - EventSink sink = new TestSink(onError: expectAsync2((e, s) { + Result result = ErrorResult("BAD", stack); + EventSink sink = TestSink(onError: expectAsync2((e, s) { expect(e, equals("BAD")); expect(s, same(stack)); })); @@ -96,7 +96,7 @@ void main() { }); test("value as future", () { - Result result = new ValueResult(42); + Result result = ValueResult(42); result.asFuture.then(expectAsync1((int v) { expect(v, equals(42)); }), onError: (e, s) { @@ -105,7 +105,7 @@ void main() { }); test("error as future", () { - Result result = new ErrorResult("BAD", stack); + Result result = ErrorResult("BAD", stack); result.asFuture.then((bool v) { fail("Unexpected value $v"); }, onError: expectAsync2((e, s) { @@ -115,7 +115,7 @@ void main() { }); test("capture future value", () { - Future value = new Future.value(42); + Future value = Future.value(42); Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isTrue); expect(result.isError, isFalse); @@ -127,7 +127,7 @@ void main() { }); test("capture future error", () { - Future value = new Future.error("BAD", stack); + Future value = Future.error("BAD", stack); Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isFalse); expect(result.isError, isTrue); @@ -141,7 +141,7 @@ void main() { test("release future value", () { Future> future = - new Future>.value(new Result.value(42)); + Future>.value(Result.value(42)); Result.release(future).then(expectAsync1((v) { expect(v, equals(42)); }), onError: (e, s) { @@ -152,7 +152,7 @@ void main() { test("release future error", () { // An error in the result is unwrapped and reified by release. Future> future = - new Future>.value(new Result.error("BAD", stack)); + Future>.value(Result.error("BAD", stack)); Result.release(future).then((v) { fail("Unexpected value: $v"); }, onError: expectAsync2((e, s) { @@ -163,7 +163,7 @@ void main() { test("release future real error", () { // An error in the error lane is passed through by release. - Future> future = new Future>.error("BAD", stack); + Future> future = Future>.error("BAD", stack); Result.release(future).then((v) { fail("Unexpected value: $v"); }, onError: expectAsync2((e, s) { @@ -173,13 +173,10 @@ void main() { }); test("capture stream", () { - var c = new StreamController(); + var c = StreamController(); var stream = Result.captureStream(c.stream); - var expectedList = new Queue.from([ - new Result.value(42), - new Result.error("BAD", stack), - new Result.value(37) - ]); + var expectedList = Queue.from( + [Result.value(42), Result.error("BAD", stack), Result.value(37)]); void listener(Result actual) { expect(expectedList.isEmpty, isFalse); expectResult(actual, expectedList.removeFirst()); @@ -194,16 +191,15 @@ void main() { }); test("release stream", () { - StreamController> c = new StreamController>(); + StreamController> c = StreamController>(); Stream stream = Result.releaseStream(c.stream); var events = [ - new Result.value(42), - new Result.error("BAD", stack), - new Result.value(37) + Result.value(42), + Result.error("BAD", stack), + Result.value(37) ]; // Expect the data events, and an extra error event. - var expectedList = new Queue.from(events) - ..add(new Result.error("BAD2", stack)); + var expectedList = Queue.from(events)..add(Result.error("BAD2", stack)); void dataListener(int v) { expect(expectedList.isEmpty, isFalse); @@ -231,7 +227,7 @@ void main() { }); test("release stream cancel on error", () { - StreamController> c = new StreamController>(); + StreamController> c = StreamController>(); Stream stream = Result.releaseStream(c.stream); stream.listen(expectAsync1((v) { expect(v, equals(42)); @@ -241,34 +237,34 @@ void main() { }), onDone: () { fail("Unexpected done event"); }, cancelOnError: true); - c.add(new Result.value(42)); - c.add(new Result.error("BAD", stack)); - c.add(new Result.value(37)); + c.add(Result.value(42)); + c.add(Result.error("BAD", stack)); + c.add(Result.value(37)); c.close(); }); test("flatten error 1", () { - Result error = new Result.error("BAD", stack); + Result error = Result.error("BAD", stack); Result flattened = - Result.flatten(new Result>.error("BAD", stack)); + Result.flatten(Result>.error("BAD", stack)); expectResult(flattened, error); }); test("flatten error 2", () { - Result error = new Result.error("BAD", stack); - Result> result = new Result>.value(error); + Result error = Result.error("BAD", stack); + Result> result = Result>.value(error); Result flattened = Result.flatten(result); expectResult(flattened, error); }); test("flatten value", () { Result> result = - new Result>.value(new Result.value(42)); - expectResult(Result.flatten(result), new Result.value(42)); + Result>.value(Result.value(42)); + expectResult(Result.flatten(result), Result.value(42)); }); test("handle unary", () { - ErrorResult result = new Result.error("error", stack); + ErrorResult result = Result.error("error", stack); bool called = false; result.handle((error) { called = true; @@ -278,7 +274,7 @@ void main() { }); test("handle binary", () { - ErrorResult result = new Result.error("error", stack); + ErrorResult result = Result.error("error", stack); bool called = false; result.handle((error, stackTrace) { called = true; @@ -289,7 +285,7 @@ void main() { }); test("handle unary and binary", () { - ErrorResult result = new Result.error("error", stack); + ErrorResult result = Result.error("error", stack); bool called = false; result.handle((error, [stackTrace]) { called = true; @@ -300,7 +296,7 @@ void main() { }); test("handle neither unary nor binary", () { - ErrorResult result = new Result.error("error", stack); + ErrorResult result = Result.error("error", stack); expect(() => result.handle(() => fail("unreachable")), throwsA(anything)); expect(() => result.handle((a, b, c) => fail("unreachable")), throwsA(anything)); @@ -332,9 +328,9 @@ class TestSink implements EventSink { final Function onDone; TestSink( - {void this.onData(T data): _nullData, - void this.onError(e, StackTrace s): _nullError, - void this.onDone(): _nullDone}); + {void this.onData(T data) = _nullData, + void this.onError(e, StackTrace s) = _nullError, + void this.onDone() = _nullDone}); void add(T value) { onData(value); diff --git a/pkgs/async/test/single_subscription_transformer_test.dart b/pkgs/async/test/single_subscription_transformer_test.dart index 74e462b3..95b321b9 100644 --- a/pkgs/async/test/single_subscription_transformer_test.dart +++ b/pkgs/async/test/single_subscription_transformer_test.dart @@ -11,7 +11,7 @@ import 'utils.dart'; void main() { test("buffers events as soon as it's bound", () async { - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); var stream = controller.stream.transform(const SingleSubscriptionTransformer()); @@ -31,7 +31,7 @@ void main() { test("cancels the subscription to the broadcast stream when it's canceled", () async { var canceled = false; - var controller = new StreamController.broadcast(onCancel: () { + var controller = StreamController.broadcast(onCancel: () { canceled = true; }); var stream = diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 55b4ce34..fb1f4a57 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -11,13 +11,13 @@ import "utils.dart"; main() { test("a stream is linked before listening", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.setSourceStream(createStream()); expect(completer.stream.toList(), completion([1, 2, 3, 4])); }); test("listened to before a stream is linked", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); var done = completer.stream.toList(); await flushMicrotasks(); completer.setSourceStream(createStream()); @@ -25,20 +25,20 @@ main() { }); test("cancel before linking a stream doesn't listen on stream", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); var subscription = completer.stream.listen(null); subscription.pause(); // Should be ignored. subscription.cancel(); - completer.setSourceStream(new UnusableStream()); // Doesn't throw. + completer.setSourceStream(UnusableStream()); // Doesn't throw. }); test("listen and pause before linking stream", () async { - var controller = new StreamCompleter(); + var controller = StreamCompleter(); var events = []; var subscription = controller.stream.listen(events.add); var done = subscription.asFuture(); subscription.pause(); - var sourceController = new StreamController(); + var sourceController = StreamController(); sourceController..add(1)..add(2)..add(3)..add(4); controller.setSourceStream(sourceController.stream); await flushMicrotasks(); @@ -56,7 +56,7 @@ main() { }); test("pause more than once", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); var events = []; var subscription = completer.stream.listen(events.add); var done = subscription.asFuture(); @@ -74,9 +74,9 @@ main() { }); test("cancel new stream before source is done", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); var lastEvent = -1; - var controller = new StreamController(); + var controller = StreamController(); StreamSubscription subscription; subscription = completer.stream.listen((value) { expect(value, lessThan(3)); @@ -106,17 +106,17 @@ main() { }); test("complete with setEmpty before listening", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.setEmpty(); - var done = new Completer(); + var done = Completer(); completer.stream.listen(unreachable("data"), onError: unreachable("error"), onDone: done.complete); await done.future; }); test("complete with setEmpty after listening", () async { - var completer = new StreamCompleter(); - var done = new Completer(); + var completer = StreamCompleter(); + var done = Completer(); completer.stream.listen(unreachable("data"), onError: unreachable("error"), onDone: done.complete); completer.setEmpty(); @@ -124,9 +124,9 @@ main() { }); test("source stream isn't listened to until completer stream is", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); StreamController controller; - controller = new StreamController(onListen: () { + controller = StreamController(onListen: () { scheduleMicrotask(controller.close); }); @@ -139,9 +139,9 @@ main() { }); test("cancelOnError true when listening before linking stream", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); Object lastEvent = -1; - var controller = new StreamController(); + var controller = StreamController(); completer.stream.listen((value) { expect(value, lessThan(3)); lastEvent = value; @@ -172,9 +172,9 @@ main() { }); test("cancelOnError true when listening after linking stream", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); Object lastEvent = -1; - var controller = new StreamController(); + var controller = StreamController(); completer.setSourceStream(controller.stream); controller.add(1); expect(controller.hasListener, isFalse); @@ -204,7 +204,7 @@ main() { }); test("linking a stream after setSourceStream before listen", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.setSourceStream(createStream()); expect(() => completer.setSourceStream(createStream()), throwsStateError); expect(() => completer.setEmpty(), throwsStateError); @@ -215,7 +215,7 @@ main() { }); test("linking a stream after setSourceStream after listen", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); var list = completer.stream.toList(); completer.setSourceStream(createStream()); expect(() => completer.setSourceStream(createStream()), throwsStateError); @@ -227,7 +227,7 @@ main() { }); test("linking a stream after setEmpty before listen", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.setEmpty(); expect(() => completer.setSourceStream(createStream()), throwsStateError); expect(() => completer.setEmpty(), throwsStateError); @@ -238,7 +238,7 @@ main() { }); test("linking a stream after setEmpty() after listen", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); var list = completer.stream.toList(); completer.setEmpty(); expect(() => completer.setSourceStream(createStream()), throwsStateError); @@ -250,7 +250,7 @@ main() { }); test("listening more than once after setting stream", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.setSourceStream(createStream()); var list = completer.stream.toList(); expect(() => completer.stream.toList(), throwsStateError); @@ -259,14 +259,14 @@ main() { }); test("listening more than once before setting stream", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.stream.toList(); expect(() => completer.stream.toList(), throwsStateError); }); test("setting onData etc. before and after setting stream", () async { - var completer = new StreamCompleter(); - var controller = new StreamController(); + var completer = StreamCompleter(); + var controller = StreamController(); var subscription = completer.stream.listen(null); Object lastEvent = 0; subscription.onData((value) => lastEvent = value); @@ -294,8 +294,8 @@ main() { }); test("pause w/ resume future accross setting stream", () async { - var completer = new StreamCompleter(); - var resume = new Completer(); + var completer = StreamCompleter(); + var resume = Completer(); var subscription = completer.stream.listen(unreachable("data")); subscription.pause(resume.future); await flushMicrotasks(); @@ -309,8 +309,8 @@ main() { }); test("asFuture with error accross setting stream", () async { - var completer = new StreamCompleter(); - var controller = new StreamController(); + var completer = StreamCompleter(); + var controller = StreamController(); var subscription = completer.stream.listen(unreachable("data"), cancelOnError: false); var done = subscription.asFuture(); @@ -327,7 +327,7 @@ main() { group("setError()", () { test("produces a stream that emits a single error", () { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.stream.listen(unreachable("data"), onError: expectAsync2((error, stackTrace) { expect(error, equals("oh no")); @@ -338,7 +338,7 @@ main() { test("produces a stream that emits a single error on a later listen", () async { - var completer = new StreamCompleter(); + var completer = StreamCompleter(); completer.setError("oh no"); await flushMicrotasks(); diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 83b685c3..3c4a3a87 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -11,16 +11,16 @@ main() { group("single-subscription", () { StreamGroup streamGroup; setUp(() { - streamGroup = new StreamGroup(); + streamGroup = StreamGroup(); }); test("buffers events from multiple sources", () async { - var controller1 = new StreamController(); + var controller1 = StreamController(); streamGroup.add(controller1.stream); controller1.add("first"); controller1.close(); - var controller2 = new StreamController(); + var controller2 = StreamController(); streamGroup.add(controller2.stream); controller2.add("second"); controller2.close(); @@ -34,12 +34,12 @@ main() { }); test("buffers errors from multiple sources", () async { - var controller1 = new StreamController(); + var controller1 = StreamController(); streamGroup.add(controller1.stream); controller1.addError("first"); controller1.close(); - var controller2 = new StreamController(); + var controller2 = StreamController(); streamGroup.add(controller2.stream); controller2.addError("second"); controller2.close(); @@ -49,14 +49,14 @@ main() { expect(streamGroup.close(), completes); var transformed = streamGroup.stream.transform( - new StreamTransformer.fromHandlers( + StreamTransformer.fromHandlers( handleError: (error, _, sink) => sink.add("error: $error"))); expect(transformed.toList(), completion(equals(["error: first", "error: second"]))); }); test("buffers events and errors together", () async { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); controller.add("first"); @@ -72,7 +72,7 @@ main() { expect(streamGroup.close(), completes); var transformed = streamGroup.stream.transform( - new StreamTransformer.fromHandlers( + StreamTransformer.fromHandlers( handleData: (data, sink) => sink.add("data: $data"), handleError: (error, _, sink) => sink.add("error: $error"))); expect( @@ -88,7 +88,7 @@ main() { }); test("emits events once there's a listener", () { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); expect( @@ -102,7 +102,7 @@ main() { }); test("doesn't buffer events from a broadcast stream", () async { - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); streamGroup.add(controller.stream); controller.add("first"); @@ -116,7 +116,7 @@ main() { }); test("when paused, buffers events from a broadcast stream", () async { - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); streamGroup.add(controller.stream); var events = []; @@ -136,7 +136,7 @@ main() { }); test("emits events from a broadcast stream once there's a listener", () { - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); streamGroup.add(controller.stream); expect( @@ -152,8 +152,7 @@ main() { test("forwards cancel errors", () async { var subscription = streamGroup.stream.listen(null); - var controller = - new StreamController(onCancel: () => throw "error"); + var controller = StreamController(onCancel: () => throw "error"); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -163,9 +162,9 @@ main() { test("forwards a cancel future", () async { var subscription = streamGroup.stream.listen(null); - var completer = new Completer(); + var completer = Completer(); var controller = - new StreamController(onCancel: () => completer.future); + StreamController(onCancel: () => completer.future); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -187,7 +186,7 @@ main() { await flushMicrotasks(); var paused = false; - var controller = new StreamController( + var controller = StreamController( onPause: () => paused = true, onResume: () => paused = false); subscription.pause(); @@ -211,7 +210,7 @@ main() { test("immediately listens to and cancels the stream", () async { var listened = false; var canceled = false; - var controller = new StreamController(onListen: () { + var controller = StreamController(onListen: () { listened = true; }, onCancel: expectAsync0(() { expect(listened, isTrue); @@ -226,15 +225,15 @@ main() { test("forwards cancel errors", () { var controller = - new StreamController(onCancel: () => throw "error"); + StreamController(onCancel: () => throw "error"); expect(streamGroup.add(controller.stream), throwsA("error")); }); test("forwards a cancel future", () async { - var completer = new Completer(); + var completer = Completer(); var controller = - new StreamController(onCancel: () => completer.future); + StreamController(onCancel: () => completer.future); var fired = false; streamGroup.add(controller.stream).then((_) => fired = true); @@ -252,16 +251,16 @@ main() { group("broadcast", () { StreamGroup streamGroup; setUp(() { - streamGroup = new StreamGroup.broadcast(); + streamGroup = StreamGroup.broadcast(); }); test("buffers events from multiple sources", () async { - var controller1 = new StreamController(); + var controller1 = StreamController(); streamGroup.add(controller1.stream); controller1.add("first"); controller1.close(); - var controller2 = new StreamController(); + var controller2 = StreamController(); streamGroup.add(controller2.stream); controller2.add("second"); controller2.close(); @@ -275,10 +274,10 @@ main() { }); test("emits events from multiple sources once there's a listener", () { - var controller1 = new StreamController(); + var controller1 = StreamController(); streamGroup.add(controller1.stream); - var controller2 = new StreamController(); + var controller2 = StreamController(); streamGroup.add(controller2.stream); expect( @@ -294,7 +293,7 @@ main() { test("doesn't buffer events once a listener has been added and removed", () async { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); streamGroup.stream.listen(null).cancel(); @@ -311,7 +310,7 @@ main() { }); test("doesn't buffer events from a broadcast stream", () async { - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); streamGroup.add(controller.stream); controller.add("first"); controller.addError("second"); @@ -324,7 +323,7 @@ main() { }); test("emits events from a broadcast stream once there's a listener", () { - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); streamGroup.add(controller.stream); expect( @@ -340,7 +339,7 @@ main() { test("cancels and re-listens broadcast streams", () async { var subscription = streamGroup.stream.listen(null); - var controller = new StreamController.broadcast(); + var controller = StreamController.broadcast(); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -359,7 +358,7 @@ main() { var subscription = streamGroup.stream.listen(null); var controller = - new StreamController(onCancel: expectAsync0(() {}, count: 0)); + StreamController(onCancel: expectAsync0(() {}, count: 0)); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -376,7 +375,7 @@ main() { var events = []; var subscription = streamGroup.stream.listen(events.add); - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -396,7 +395,7 @@ main() { }); test("a single-subscription stream can be removed while dormant", () async { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -415,17 +414,17 @@ main() { group("regardless of type", () { group("single-subscription", () { - regardlessOfType(() => new StreamGroup()); + regardlessOfType(() => StreamGroup()); }); group("broadcast", () { - regardlessOfType(() => new StreamGroup.broadcast()); + regardlessOfType(() => StreamGroup.broadcast()); }); }); test("merge() emits events from all components streams", () { - var controller1 = new StreamController(); - var controller2 = new StreamController(); + var controller1 = StreamController(); + var controller2 = StreamController(); var merged = StreamGroup.merge([controller1.stream, controller2.stream]); @@ -448,7 +447,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { group("while dormant", () { test("doesn't listen to the stream until the group is listened to", () async { - var controller = new StreamController(); + var controller = StreamController(); expect(streamGroup.add(controller.stream), isNull); await flushMicrotasks(); @@ -460,7 +459,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("is a no-op if the stream is already in the group", () { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); streamGroup.add(controller.stream); streamGroup.add(controller.stream); @@ -478,7 +477,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("listens to the stream immediately", () async { - var controller = new StreamController(); + var controller = StreamController(); expect(streamGroup.add(controller.stream), isNull); await flushMicrotasks(); @@ -486,7 +485,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("is a no-op if the stream is already in the group", () async { - var controller = new StreamController(); + var controller = StreamController(); // If the stream were actually listened to more than once, future // calls to [add] would throw [StateError]s. @@ -500,7 +499,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { group("remove()", () { group("while dormant", () { test("stops emitting events for a stream that's removed", () async { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); expect(streamGroup.stream.toList(), completion(equals(["first"]))); @@ -514,14 +513,14 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("is a no-op for an unknown stream", () { - var controller = new StreamController(); + var controller = StreamController(); expect(streamGroup.remove(controller.stream), isNull); }); test("and closed closes the group when the last stream is removed", () async { - var controller1 = new StreamController(); - var controller2 = new StreamController(); + var controller1 = StreamController(); + var controller2 = StreamController(); streamGroup.add(controller1.stream); streamGroup.add(controller2.stream); @@ -541,7 +540,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { group("while listening", () { test("doesn't emit events from a removed stream", () { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); // The subscription to [controller.stream] is canceled synchronously, so @@ -557,7 +556,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("cancels the stream's subscription", () async { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); streamGroup.stream.listen(null); @@ -571,7 +570,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { test("forwards cancel errors", () async { var controller = - new StreamController(onCancel: () => throw "error"); + StreamController(onCancel: () => throw "error"); streamGroup.add(controller.stream); streamGroup.stream.listen(null); @@ -581,9 +580,9 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("forwards cancel futures", () async { - var completer = new Completer(); + var completer = Completer(); var controller = - new StreamController(onCancel: () => completer.future); + StreamController(onCancel: () => completer.future); streamGroup.stream.listen(null); await flushMicrotasks(); @@ -603,7 +602,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); test("is a no-op for an unknown stream", () async { - var controller = new StreamController(); + var controller = StreamController(); streamGroup.stream.listen(null); await flushMicrotasks(); @@ -616,8 +615,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { streamGroup.stream.listen(null, onDone: () => done = true); await flushMicrotasks(); - var controller1 = new StreamController(); - var controller2 = new StreamController(); + var controller1 = StreamController(); + var controller2 = StreamController(); streamGroup.add(controller1.stream); streamGroup.add(controller2.stream); @@ -646,8 +645,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { test( "if there are streams, closes the group once those streams close " "and there's a listener", () async { - var controller1 = new StreamController(); - var controller2 = new StreamController(); + var controller1 = StreamController(); + var controller2 = StreamController(); streamGroup.add(controller1.stream); streamGroup.add(controller2.stream); @@ -673,8 +672,8 @@ void regardlessOfType(StreamGroup newStreamGroup()) { streamGroup.stream.listen(null, onDone: () => done = true); await flushMicrotasks(); - var controller1 = new StreamController(); - var controller2 = new StreamController(); + var controller1 = StreamController(); + var controller2 = StreamController(); streamGroup.add(controller1.stream); streamGroup.add(controller2.stream); @@ -699,7 +698,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { var events = []; streamGroup.stream.listen(events.add); - var controller = new StreamController(); + var controller = StreamController(); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -721,4 +720,4 @@ void regardlessOfType(StreamGroup newStreamGroup()) { } /// Wait for all microtasks to complete. -Future flushMicrotasks() => new Future.delayed(Duration.zero); +Future flushMicrotasks() => Future.delayed(Duration.zero); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 37dedb94..aaf12270 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -12,8 +12,8 @@ import "utils.dart"; main() { group("source stream", () { test("is listened to on first request, paused between requests", () async { - var controller = new StreamController(); - var events = new StreamQueue(controller.stream); + var controller = StreamController(); + var events = StreamQueue(controller.stream); await flushMicrotasks(); expect(controller.hasListener, isFalse); @@ -44,7 +44,7 @@ main() { group("eventsDispatched", () { test("increments after a next future completes", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(events.eventsDispatched, equals(0)); await flushMicrotasks(); @@ -61,13 +61,13 @@ main() { }); test("increments multiple times for multi-value requests", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); await events.take(3); expect(events.eventsDispatched, equals(3)); }); test("increments multiple times for an accepted transaction", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); await events.withTransaction((queue) async { await queue.next; await queue.next; @@ -77,7 +77,7 @@ main() { }); test("doesn't increment for rest requests", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); await events.rest.toList(); expect(events.eventsDispatched, equals(0)); }); @@ -85,7 +85,7 @@ main() { group("lookAhead operation", () { test("as simple list of events", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.lookAhead(4), [1, 2, 3, 4]); expect(await events.next, 1); expect(await events.lookAhead(2), [2, 3]); @@ -95,7 +95,7 @@ main() { }); test("of 0 events", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(events.lookAhead(0), completion([])); expect(events.next, completion(1)); expect(events.lookAhead(0), completion([])); @@ -111,7 +111,7 @@ main() { }); test("with bad arguments throws", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(() => events.lookAhead(-1), throwsArgumentError); expect(await events.next, 1); // Did not consume event. expect(() => events.lookAhead(-1), throwsArgumentError); @@ -120,13 +120,13 @@ main() { }); test("of too many arguments", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.lookAhead(6), [1, 2, 3, 4]); await events.cancel(); }); test("too large later", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.lookAhead(6), [3, 4]); @@ -134,7 +134,7 @@ main() { }); test("error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(events.lookAhead(4), throwsA("To err is divine!")); expect(events.take(4), throwsA("To err is divine!")); expect(await events.next, 4); @@ -144,7 +144,7 @@ main() { group("next operation", () { test("simple sequence of requests", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); for (int i = 1; i <= 4; i++) { expect(await events.next, i); } @@ -152,7 +152,7 @@ main() { }); test("multiple requests at the same time", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var result = await Future.wait( [events.next, events.next, events.next, events.next]); expect(result, [1, 2, 3, 4]); @@ -160,7 +160,7 @@ main() { }); test("sequence of requests with error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(await events.next, 1); expect(await events.next, 2); expect(events.next, throwsA("To err is divine!")); @@ -171,7 +171,7 @@ main() { group("skip operation", () { test("of two elements in the middle of sequence", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.skip(2), 0); expect(await events.next, 4); @@ -179,7 +179,7 @@ main() { }); test("with negative/bad arguments throws", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(() => events.skip(-1), throwsArgumentError); // A non-int throws either a type error or an argument error, // depending on whether it's checked mode or not. @@ -190,7 +190,7 @@ main() { }); test("of 0 elements works", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(events.skip(0), completion(0)); expect(events.next, completion(1)); expect(events.skip(0), completion(0)); @@ -206,13 +206,13 @@ main() { }); test("of too many events ends at stream start", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.skip(6), 2); await events.cancel(); }); test("of too many events after some events", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.skip(6), 4); @@ -220,7 +220,7 @@ main() { }); test("of too many events ends at stream end", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.next, 3); @@ -230,20 +230,20 @@ main() { }); test("of events with error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(events.skip(4), throwsA("To err is divine!")); expect(await events.next, 4); await events.cancel(); }); test("of events with error, and skip again after", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(events.skip(4), throwsA("To err is divine!")); expect(events.skip(2), completion(1)); await events.cancel(); }); test("multiple skips at same time complete in order.", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var skip1 = events.skip(1); var skip2 = events.skip(0); var skip3 = events.skip(4); @@ -267,7 +267,7 @@ main() { group("take operation", () { test("as simple take of events", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.take(2), [2, 3]); expect(await events.next, 4); @@ -275,7 +275,7 @@ main() { }); test("of 0 events", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(events.take(0), completion([])); expect(events.next, completion(1)); expect(events.take(0), completion([])); @@ -291,7 +291,7 @@ main() { }); test("with bad arguments throws", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(() => events.take(-1), throwsArgumentError); expect(await events.next, 1); // Did not consume event. expect(() => events.take(-1), throwsArgumentError); @@ -300,13 +300,13 @@ main() { }); test("of too many arguments", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.take(6), [1, 2, 3, 4]); await events.cancel(); }); test("too large later", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.take(6), [3, 4]); @@ -314,7 +314,7 @@ main() { }); test("error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(events.take(4), throwsA("To err is divine!")); expect(await events.next, 4); await events.cancel(); @@ -323,18 +323,18 @@ main() { group("rest operation", () { test("after single next", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.rest.toList(), [2, 3, 4]); }); test("at start", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.rest.toList(), [1, 2, 3, 4]); }); test("at end", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.next, 3); @@ -343,7 +343,7 @@ main() { }); test("after end", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.next, 3); @@ -353,7 +353,7 @@ main() { }); test("after receiving done requested before", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var next1 = events.next; var next2 = events.next; var next3 = events.next; @@ -368,17 +368,17 @@ main() { }); test("with an error event error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(await events.next, 1); var rest = events.rest; - var events2 = new StreamQueue(rest); + var events2 = StreamQueue(rest); expect(await events2.next, 2); expect(events2.next, throwsA("To err is divine!")); expect(await events2.next, 4); }); test("closes the events, prevents other operations", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var stream = events.rest; expect(() => events.next, throwsStateError); expect(() => events.skip(1), throwsStateError); @@ -389,9 +389,9 @@ main() { }); test("forwards to underlying stream", () async { - var cancel = new Completer(); - var controller = new StreamController(onCancel: () => cancel.future); - var events = new StreamQueue(controller.stream); + var cancel = Completer(); + var controller = StreamController(onCancel: () => cancel.future); + var events = StreamQueue(controller.stream); expect(controller.hasListener, isFalse); var next = events.next; expect(controller.hasListener, isTrue); @@ -437,7 +437,7 @@ main() { group("peek operation", () { test("peeks one event", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.peek, 1); expect(await events.next, 1); expect(await events.peek, 2); @@ -449,14 +449,14 @@ main() { await events.cancel(); }); test("multiple requests at the same time", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var result = await Future.wait( [events.peek, events.peek, events.next, events.peek, events.peek]); expect(result, [1, 1, 1, 2, 2]); await events.cancel(); }); test("sequence of requests with error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(await events.next, 1); expect(await events.next, 2); expect(events.peek, throwsA("To err is divine!")); @@ -470,7 +470,7 @@ main() { group("cancel operation", () { test("closes the events, prevents any other operation", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); await events.cancel(); expect(() => events.lookAhead(1), throwsStateError); expect(() => events.next, throwsStateError); @@ -483,16 +483,16 @@ main() { test("cancels underlying subscription when called before any event", () async { - var cancelFuture = new Future.value(42); - var controller = new StreamController(onCancel: () => cancelFuture); - var events = new StreamQueue(controller.stream); + var cancelFuture = Future.value(42); + var controller = StreamController(onCancel: () => cancelFuture); + var events = StreamQueue(controller.stream); expect(await events.cancel(), 42); }); test("cancels underlying subscription, returns result", () async { - var cancelFuture = new Future.value(42); - var controller = new StreamController(onCancel: () => cancelFuture); - var events = new StreamQueue(controller.stream); + var cancelFuture = Future.value(42); + var controller = StreamController(onCancel: () => cancelFuture); + var events = StreamQueue(controller.stream); controller.add(1); expect(await events.next, 1); expect(await events.cancel(), 42); @@ -500,7 +500,7 @@ main() { group("with immediate: true", () { test("closes the events, prevents any other operation", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); await events.cancel(immediate: true); expect(() => events.next, throwsStateError); expect(() => events.skip(1), throwsStateError); @@ -510,10 +510,10 @@ main() { }); test("cancels the underlying subscription immediately", () async { - var controller = new StreamController(); + var controller = StreamController(); controller.add(1); - var events = new StreamQueue(controller.stream); + var events = StreamQueue(controller.stream); expect(await events.next, 1); expect(controller.hasListener, isTrue); @@ -523,16 +523,15 @@ main() { test("cancels the underlying subscription when called before any event", () async { - var cancelFuture = new Future.value(42); - var controller = - new StreamController(onCancel: () => cancelFuture); + var cancelFuture = Future.value(42); + var controller = StreamController(onCancel: () => cancelFuture); - var events = new StreamQueue(controller.stream); + var events = StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); }); test("closes pending requests", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(events.next, throwsStateError); expect(events.hasNext, completion(isFalse)); @@ -543,8 +542,8 @@ main() { test("returns the result of closing the underlying subscription", () async { var controller = - new StreamController(onCancel: () => new Future.value(42)); - var events = new StreamQueue(controller.stream); + StreamController(onCancel: () => Future.value(42)); + var events = StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); }); @@ -552,8 +551,8 @@ main() { () async { var wasListened = false; var controller = - new StreamController(onListen: () => wasListened = true); - var events = new StreamQueue(controller.stream); + StreamController(onListen: () => wasListened = true); + var events = StreamQueue(controller.stream); expect(wasListened, isFalse); expect(controller.hasListener, isFalse); @@ -566,18 +565,18 @@ main() { group("hasNext operation", () { test("true at start", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.hasNext, isTrue); }); test("true after start", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, isTrue); }); test("true at end", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); for (int i = 1; i <= 4; i++) { expect(await events.next, i); } @@ -585,7 +584,7 @@ main() { }); test("true when enqueued", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var values = []; for (int i = 1; i <= 3; i++) { events.next.then(values.add); @@ -596,7 +595,7 @@ main() { }); test("false when enqueued", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var values = []; for (int i = 1; i <= 4; i++) { events.next.then(values.add); @@ -607,8 +606,8 @@ main() { }); test("true when data event", () async { - var controller = new StreamController(); - var events = new StreamQueue(controller.stream); + var controller = StreamController(); + var events = StreamQueue(controller.stream); bool hasNext; events.hasNext.then((result) { @@ -623,8 +622,8 @@ main() { }); test("true when error event", () async { - var controller = new StreamController(); - var events = new StreamQueue(controller.stream); + var controller = StreamController(); + var events = StreamQueue(controller.stream); bool hasNext; events.hasNext.then((result) { @@ -640,7 +639,7 @@ main() { }); test("- hasNext after hasNext", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.hasNext, true); expect(await events.hasNext, true); expect(await events.next, 1); @@ -658,7 +657,7 @@ main() { }); test("- next after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.next, 2); @@ -666,7 +665,7 @@ main() { }); test("- next after true, enqueued", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); var responses = []; events.next.then(responses.add); events.hasNext.then(responses.add); @@ -678,7 +677,7 @@ main() { }); test("- skip 0 after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.skip(0), 0); @@ -686,7 +685,7 @@ main() { }); test("- skip 1 after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.skip(1), 0); @@ -694,7 +693,7 @@ main() { }); test("- skip 2 after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.skip(2), 0); @@ -702,7 +701,7 @@ main() { }); test("- take 0 after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.take(0), isEmpty); @@ -710,7 +709,7 @@ main() { }); test("- take 1 after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.take(1), [2]); @@ -718,7 +717,7 @@ main() { }); test("- take 2 after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); expect(await events.take(2), [2, 3]); @@ -726,7 +725,7 @@ main() { }); test("- rest after true", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); var stream = events.rest; @@ -734,7 +733,7 @@ main() { }); test("- rest after true, at last", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.next, 3); @@ -744,7 +743,7 @@ main() { }); test("- rest after false", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.next, 3); @@ -755,7 +754,7 @@ main() { }); test("- cancel after true on data", () async { - var events = new StreamQueue(createStream()); + var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.hasNext, true); @@ -763,7 +762,7 @@ main() { }); test("- cancel after true on error", () async { - var events = new StreamQueue(createErrorStream()); + var events = StreamQueue(createErrorStream()); expect(await events.next, 1); expect(await events.next, 2); expect(await events.hasNext, true); @@ -777,7 +776,7 @@ main() { StreamQueue queue1; StreamQueue queue2; setUp(() async { - events = new StreamQueue(createStream()); + events = StreamQueue(createStream()); expect(await events.next, 1); transaction = events.startTransaction(); queue1 = transaction.newQueue(); @@ -808,7 +807,7 @@ main() { }); test("independently emit errors", () async { - events = new StreamQueue(createErrorStream()); + events = StreamQueue(createErrorStream()); expect(await events.next, 1); transaction = events.startTransaction(); queue1 = transaction.newQueue(); @@ -872,8 +871,8 @@ main() { // Regression test. test("pending child rest requests emit no more events", () async { - var controller = new StreamController(); - var events = new StreamQueue(controller.stream); + var controller = StreamController(); + var events = StreamQueue(controller.stream); var transaction = events.startTransaction(); var queue = transaction.newQueue(); @@ -906,8 +905,8 @@ main() { }); test("before the transaction emits any events, does nothing", () async { - var controller = new StreamController(); - var events = new StreamQueue(controller.stream); + var controller = StreamController(); + var events = StreamQueue(controller.stream); // Queue a request before the transaction, but don't let it complete // until we're done with the transaction. @@ -983,8 +982,8 @@ main() { }); test("before the transaction emits any events, does nothing", () async { - var controller = new StreamController(); - var events = new StreamQueue(controller.stream); + var controller = StreamController(); + var events = StreamQueue(controller.stream); // Queue a request before the transaction, but don't let it complete // until we're done with the transaction. @@ -1006,7 +1005,7 @@ main() { group("withTransaction operation", () { StreamQueue events; setUp(() async { - events = new StreamQueue(createStream()); + events = StreamQueue(createStream()); expect(await events.next, 1); }); @@ -1060,7 +1059,7 @@ main() { group("cancelable operation", () { StreamQueue events; setUp(() async { - events = new StreamQueue(createStream()); + events = StreamQueue(createStream()); expect(await events.next, 1); }); @@ -1120,7 +1119,7 @@ main() { // doing rest. Each of the first rounds do 10 events of each type, // the rest does 20 elements. var eventCount = 20 * (3 * 3 + 1); - var events = new StreamQueue(createLongStream(eventCount)); + var events = StreamQueue(createLongStream(eventCount)); // Test expecting [startIndex .. startIndex + 9] as events using // `next`. @@ -1139,7 +1138,7 @@ main() { // `take(10)`. takeTest(startIndex) { expect(events.take(10), - completion(new List.generate(10, (i) => startIndex + i))); + completion(List.generate(10, (i) => startIndex + i))); } var tests = [nextTest, skipTest, takeTest]; @@ -1155,11 +1154,11 @@ main() { } // Then expect 20 more events as a `rest` call. expect(events.rest.toList(), - completion(new List.generate(20, (i) => counter + i))); + completion(List.generate(20, (i) => counter + i))); }); } -typedef T Func1Required(T value); +typedef Func1Required = T Function(T value); Stream createStream() async* { yield 1; @@ -1172,7 +1171,7 @@ Stream createStream() async* { } Stream createErrorStream() { - var controller = new StreamController(); + var controller = StreamController(); () async { controller.add(1); await flushMicrotasks(); diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index a149a7f9..56860ce9 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -12,12 +12,12 @@ import "utils.dart"; main() { StreamSinkCompleter completer; setUp(() { - completer = new StreamSinkCompleter(); + completer = StreamSinkCompleter(); }); group("when a stream is linked before events are added", () { test("data events are forwarded", () { - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); completer.sink..add(1)..add(2)..add(3)..add(4); @@ -28,7 +28,7 @@ main() { }); test("error events are forwarded", () { - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); completer.sink..addError("oh no")..addError("that's bad"); @@ -37,10 +37,10 @@ main() { }); test("addStream is forwarded", () async { - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); - var controller = new StreamController(); + var controller = StreamController(); completer.sink.addStream(controller.stream); controller.add(1); @@ -61,15 +61,15 @@ main() { }); test("close() is forwarded", () { - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); completer.sink.close(); expect(sink.isClosed, isTrue); }); test("the future from the inner close() is returned", () async { - var closeCompleter = new Completer(); - var sink = new TestSink(onDone: () => closeCompleter.future); + var closeCompleter = Completer(); + var sink = TestSink(onDone: () => closeCompleter.future); completer.setDestinationSink(sink); var closeCompleted = false; @@ -86,14 +86,14 @@ main() { }); test("errors are forwarded from the inner close()", () { - var sink = new TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw "oh no"); completer.setDestinationSink(sink); expect(completer.sink.done, throwsA("oh no")); expect(completer.sink.close(), throwsA("oh no")); }); test("errors aren't top-leveled if only close() is listened to", () async { - var sink = new TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw "oh no"); completer.setDestinationSink(sink); expect(completer.sink.close(), throwsA("oh no")); @@ -102,7 +102,7 @@ main() { }); test("errors aren't top-leveled if only done is listened to", () async { - var sink = new TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw "oh no"); completer.setDestinationSink(sink); completer.sink.close(); expect(completer.sink.done, throwsA("oh no")); @@ -117,7 +117,7 @@ main() { completer.sink..add(1)..add(2)..add(3)..add(4); await flushMicrotasks(); - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); await flushMicrotasks(); @@ -131,7 +131,7 @@ main() { completer.sink..addError("oh no")..addError("that's bad"); await flushMicrotasks(); - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); await flushMicrotasks(); @@ -140,7 +140,7 @@ main() { }); test("addStream is forwarded", () async { - var controller = new StreamController(); + var controller = StreamController(); completer.sink.addStream(controller.stream); controller.add(1); @@ -150,7 +150,7 @@ main() { controller.close(); await flushMicrotasks(); - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); await flushMicrotasks(); @@ -165,7 +165,7 @@ main() { completer.sink.close(); await flushMicrotasks(); - var sink = new TestSink(); + var sink = TestSink(); completer.setDestinationSink(sink); await flushMicrotasks(); @@ -179,8 +179,8 @@ main() { })); await flushMicrotasks(); - var closeCompleter = new Completer(); - var sink = new TestSink(onDone: () => closeCompleter.future); + var closeCompleter = Completer(); + var sink = TestSink(onDone: () => closeCompleter.future); completer.setDestinationSink(sink); await flushMicrotasks(); expect(closeCompleted, isFalse); @@ -195,7 +195,7 @@ main() { expect(completer.sink.close(), throwsA("oh no")); await flushMicrotasks(); - var sink = new TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw "oh no"); completer.setDestinationSink(sink); }); @@ -203,7 +203,7 @@ main() { expect(completer.sink.close(), throwsA("oh no")); await flushMicrotasks(); - var sink = new TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw "oh no"); completer.setDestinationSink(sink); // Give the event loop a chance to top-level errors if it's going to. @@ -215,7 +215,7 @@ main() { expect(completer.sink.done, throwsA("oh no")); await flushMicrotasks(); - var sink = new TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw "oh no"); completer.setDestinationSink(sink); // Give the event loop a chance to top-level errors if it's going to. @@ -228,7 +228,7 @@ main() { expect(completer.sink.close(), completes); await flushMicrotasks(); - completer.setDestinationSink(new TestSink()); + completer.setDestinationSink(TestSink()); await flushMicrotasks(); expect(completer.sink.done, completes); @@ -239,7 +239,7 @@ main() { expect(completer.sink.done, completes); await flushMicrotasks(); - completer.setDestinationSink(new TestSink()); + completer.setDestinationSink(TestSink()); await flushMicrotasks(); expect(completer.sink.close(), completes); @@ -247,14 +247,14 @@ main() { group("fromFuture()", () { test("with a successful completion", () async { - var futureCompleter = new Completer(); + var futureCompleter = Completer(); var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); sink.add(1); sink.add(2); sink.add(3); sink.close(); - var testSink = new TestSink(); + var testSink = TestSink(); futureCompleter.complete(testSink); await testSink.done; @@ -264,7 +264,7 @@ main() { }); test("with an error", () async { - var futureCompleter = new Completer(); + var futureCompleter = Completer(); var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); expect(sink.done, throwsA("oh no")); futureCompleter.completeError("oh no"); @@ -288,10 +288,8 @@ main() { }); test("doesn't allow the destination sink to be set multiple times", () { - completer.setDestinationSink(new TestSink()); - expect( - () => completer.setDestinationSink(new TestSink()), throwsStateError); - expect( - () => completer.setDestinationSink(new TestSink()), throwsStateError); + completer.setDestinationSink(TestSink()); + expect(() => completer.setDestinationSink(TestSink()), throwsStateError); + expect(() => completer.setDestinationSink(TestSink()), throwsStateError); }); } diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index 552bdaa3..5e981a64 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -12,13 +12,13 @@ import "utils.dart"; void main() { StreamController controller; setUp(() { - controller = new StreamController(); + controller = StreamController(); }); group("fromStreamTransformer", () { test("transforms data events", () { - var transformer = new StreamSinkTransformer.fromStreamTransformer( - new StreamTransformer.fromHandlers(handleData: (i, sink) { + var transformer = StreamSinkTransformer.fromStreamTransformer( + StreamTransformer.fromHandlers(handleData: (i, sink) { sink.add(i * 2); })); var sink = transformer.bind(controller.sink); @@ -35,9 +35,8 @@ void main() { }); test("transforms error events", () { - var transformer = new StreamSinkTransformer.fromStreamTransformer( - new StreamTransformer.fromHandlers( - handleError: (i, stackTrace, sink) { + var transformer = StreamSinkTransformer.fromStreamTransformer( + StreamTransformer.fromHandlers(handleError: (i, stackTrace, sink) { sink.addError((i as num) * 2, stackTrace); })); var sink = transformer.bind(controller.sink); @@ -57,8 +56,8 @@ void main() { }); test("transforms done events", () { - var transformer = new StreamSinkTransformer.fromStreamTransformer( - new StreamTransformer.fromHandlers(handleDone: (sink) { + var transformer = StreamSinkTransformer.fromStreamTransformer( + StreamTransformer.fromHandlers(handleDone: (sink) { sink.add(1); sink.close(); })); @@ -73,16 +72,16 @@ void main() { }); test("forwards the future from inner.close", () async { - var transformer = new StreamSinkTransformer.fromStreamTransformer( - new StreamTransformer.fromHandlers()); - var innerSink = new CompleterStreamSink(); + var transformer = StreamSinkTransformer.fromStreamTransformer( + StreamTransformer.fromHandlers()); + var innerSink = CompleterStreamSink(); var sink = transformer.bind(innerSink); // The futures shouldn't complete until the inner sink's close future // completes. - var doneResult = new ResultFuture(sink.done); + var doneResult = ResultFuture(sink.done); doneResult.catchError((_) {}); - var closeResult = new ResultFuture(sink.close()); + var closeResult = ResultFuture(sink.close()); closeResult.catchError((_) {}); await flushMicrotasks(); expect(doneResult.isComplete, isFalse); @@ -96,11 +95,11 @@ void main() { }); test("doesn't top-level the future from inner.close", () async { - var transformer = new StreamSinkTransformer.fromStreamTransformer( - new StreamTransformer.fromHandlers(handleData: (_, sink) { + var transformer = StreamSinkTransformer.fromStreamTransformer( + StreamTransformer.fromHandlers(handleData: (_, sink) { sink.close(); })); - var innerSink = new CompleterStreamSink(); + var innerSink = CompleterStreamSink(); var sink = transformer.bind(innerSink); // This will close the inner sink, but it shouldn't top-level the error. @@ -118,7 +117,7 @@ void main() { group("fromHandlers", () { test("transforms data events", () { var transformer = - new StreamSinkTransformer.fromHandlers(handleData: (i, sink) { + StreamSinkTransformer.fromHandlers(handleData: (i, sink) { sink.add(i * 2); }); var sink = transformer.bind(controller.sink); @@ -135,7 +134,7 @@ void main() { }); test("transforms error events", () { - var transformer = new StreamSinkTransformer.fromHandlers( + var transformer = StreamSinkTransformer.fromHandlers( handleError: (i, stackTrace, sink) { sink.addError((i as num) * 2, stackTrace); }); @@ -156,8 +155,7 @@ void main() { }); test("transforms done events", () { - var transformer = - new StreamSinkTransformer.fromHandlers(handleDone: (sink) { + var transformer = StreamSinkTransformer.fromHandlers(handleDone: (sink) { sink.add(1); sink.close(); }); @@ -172,15 +170,15 @@ void main() { }); test("forwards the future from inner.close", () async { - var transformer = new StreamSinkTransformer.fromHandlers(); - var innerSink = new CompleterStreamSink(); + var transformer = StreamSinkTransformer.fromHandlers(); + var innerSink = CompleterStreamSink(); var sink = transformer.bind(innerSink); // The futures shouldn't complete until the inner sink's close future // completes. - var doneResult = new ResultFuture(sink.done); + var doneResult = ResultFuture(sink.done); doneResult.catchError((_) {}); - var closeResult = new ResultFuture(sink.close()); + var closeResult = ResultFuture(sink.close()); closeResult.catchError((_) {}); await flushMicrotasks(); expect(doneResult.isComplete, isFalse); @@ -195,10 +193,10 @@ void main() { test("doesn't top-level the future from inner.close", () async { var transformer = - new StreamSinkTransformer.fromHandlers(handleData: (_, sink) { + StreamSinkTransformer.fromHandlers(handleData: (_, sink) { sink.close(); }); - var innerSink = new CompleterStreamSink(); + var innerSink = CompleterStreamSink(); var sink = transformer.bind(innerSink); // This will close the inner sink, but it shouldn't top-level the error. diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index c118b125..497f1257 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -11,8 +11,8 @@ main() { StreamController controller; StreamSplitter splitter; setUp(() { - controller = new StreamController(); - splitter = new StreamSplitter(controller.stream); + controller = StreamController(); + splitter = StreamSplitter(controller.stream); }); test("a branch that's created before the stream starts to replay it", @@ -289,4 +289,4 @@ main() { } /// Wait for all microtasks to complete. -Future flushMicrotasks() => new Future.delayed(Duration.zero); +Future flushMicrotasks() => Future.delayed(Duration.zero); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 018c8ab7..1e5f5249 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -17,10 +17,10 @@ Stream streamError(Stream base, int errorValue, error) { /// at periodic intervals. Stream mks(Iterable iterable) { Iterator iterator = iterable.iterator; - StreamController controller = new StreamController(); + StreamController controller = StreamController(); // Some varying time between 3 and 10 ms. int ms = ((++ctr) * 5) % 7 + 3; - new Timer.periodic(new Duration(milliseconds: ms), (Timer timer) { + Timer.periodic(Duration(milliseconds: ms), (Timer timer) { if (iterator.moveNext()) { controller.add(iterator.current); } else { @@ -38,7 +38,7 @@ main() { // Test that zipping [streams] gives the results iterated by [expectedData]. testZip(Iterable streams, Iterable expectedData) { List data = []; - Stream zip = new StreamZip(streams); + Stream zip = StreamZip(streams); zip.listen(data.add, onDone: expectAsync0(() { expect(data, equals(expectedData)); })); @@ -145,9 +145,8 @@ main() { test("Other-streams", () { Stream st1 = mks([1, 2, 3, 4, 5, 6]).where((x) => x < 4); Stream st2 = - new Stream.periodic(const Duration(milliseconds: 5), (x) => x + 4) - .take(3); - StreamController c = new StreamController.broadcast(); + Stream.periodic(const Duration(milliseconds: 5), (x) => x + 4).take(3); + StreamController c = StreamController.broadcast(); Stream st3 = c.stream; testZip([ st1, @@ -167,7 +166,7 @@ main() { test("Error 1", () { expect( - new StreamZip([ + StreamZip([ streamError(mks([1, 2, 3]), 2, "BAD-1"), mks([4, 5, 6]), mks([7, 8, 9]) @@ -177,7 +176,7 @@ main() { test("Error 2", () { expect( - new StreamZip([ + StreamZip([ mks([1, 2, 3]), streamError(mks([4, 5, 6]), 5, "BAD-2"), mks([7, 8, 9]) @@ -187,7 +186,7 @@ main() { test("Error 3", () { expect( - new StreamZip([ + StreamZip([ mks([1, 2, 3]), mks([4, 5, 6]), streamError(mks([7, 8, 9]), 8, "BAD-3") @@ -197,7 +196,7 @@ main() { test("Error at end", () { expect( - new StreamZip([ + StreamZip([ mks([1, 2, 3]), streamError(mks([4, 5, 6]), 6, "BAD-4"), mks([7, 8, 9]) @@ -209,21 +208,21 @@ main() { // StreamControllers' streams with no "close" called will never be done, // so the fourth event of the first stream is guaranteed to come first. expect( - new StreamZip([ + StreamZip([ streamError(mks([1, 2, 3, 4]), 4, "BAD-5"), - (new StreamController()..add(4)..add(5)..add(6)).stream, - (new StreamController()..add(7)..add(8)..add(9)).stream + (StreamController()..add(4)..add(5)..add(6)).stream, + (StreamController()..add(7)..add(8)..add(9)).stream ]).toList(), throwsA(equals("BAD-5"))); }); test("Error after first end", () { - StreamController controller = new StreamController(); + StreamController controller = StreamController(); controller..add(7)..add(8)..add(9); // Transformer that puts error into controller when one of the first two // streams have sent a done event. StreamTransformer trans = - new StreamTransformer.fromHandlers(handleDone: (EventSink s) { + StreamTransformer.fromHandlers(handleDone: (EventSink s) { Timer.run(() { controller.addError("BAD-6"); }); @@ -242,14 +241,14 @@ main() { test("Pause/Resume", () { int sc1p = 0; - StreamController c1 = new StreamController(onPause: () { + StreamController c1 = StreamController(onPause: () { sc1p++; }, onResume: () { sc1p--; }); int sc2p = 0; - StreamController c2 = new StreamController(onPause: () { + StreamController c2 = StreamController(onPause: () { sc2p++; }, onResume: () { sc2p--; @@ -260,12 +259,12 @@ main() { expect(sc2p, equals(0)); }); // Call to complete test. - Stream zip = new StreamZip([c1.stream, c2.stream]); + Stream zip = StreamZip([c1.stream, c2.stream]); - const ms25 = const Duration(milliseconds: 25); + const ms25 = Duration(milliseconds: 25); // StreamIterator uses pause and resume to control flow. - StreamIterator it = new StreamIterator(zip); + StreamIterator it = StreamIterator(zip); it.moveNext().then((hasMore) { expect(hasMore, isTrue); @@ -279,7 +278,7 @@ main() { }).then((hasMore) { expect(hasMore, isTrue); expect(it.current, equals([5, 6])); - new Future.delayed(ms25).then((_) { + Future.delayed(ms25).then((_) { c2.add(8); }); return it.moveNext(); @@ -303,18 +302,18 @@ main() { }); test("pause-resume2", () { - var s1 = new Stream.fromIterable([0, 2, 4, 6, 8]); - var s2 = new Stream.fromIterable([1, 3, 5, 7]); - var sz = new StreamZip([s1, s2]); + var s1 = Stream.fromIterable([0, 2, 4, 6, 8]); + var s2 = Stream.fromIterable([1, 3, 5, 7]); + var sz = StreamZip([s1, s2]); int ctr = 0; StreamSubscription sub; sub = sz.listen(expectAsync1((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); if (ctr == 1) { - sub.pause(new Future.delayed(const Duration(milliseconds: 25))); + sub.pause(Future.delayed(const Duration(milliseconds: 25))); } else if (ctr == 2) { sub.pause(); - new Future.delayed(const Duration(milliseconds: 25)).then((_) { + Future.delayed(const Duration(milliseconds: 25)).then((_) { sub.resume(); }); } diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index a0773a68..c7756e60 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -10,19 +10,19 @@ import "package:test/test.dart"; main() { StreamController controller; - controller = new StreamController(); + controller = StreamController(); testStream("singlesub-async", controller, controller.stream); - controller = new StreamController.broadcast(); + controller = StreamController.broadcast(); testStream("broadcast-async", controller, controller.stream); - controller = new StreamController(); + controller = StreamController(); testStream( "asbroadcast-async", controller, controller.stream.asBroadcastStream()); - controller = new StreamController(sync: true); + controller = StreamController(sync: true); testStream("singlesub-sync", controller, controller.stream); - controller = new StreamController.broadcast(sync: true); + controller = StreamController.broadcast(sync: true); testStream("broadcast-sync", controller, controller.stream); - controller = new StreamController(sync: true); + controller = StreamController(sync: true); testStream( "asbroadcast-sync", controller, controller.stream.asBroadcastStream()); } diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 804e92f1..9460dec5 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -13,7 +13,7 @@ main() { test("subscription stream of an entire subscription", () async { var stream = createStream(); var subscription = stream.listen(null); - var subscriptionStream = new SubscriptionStream(subscription); + var subscriptionStream = SubscriptionStream(subscription); await flushMicrotasks(); expect(subscriptionStream.toList(), completion([1, 2, 3, 4])); }); @@ -21,13 +21,13 @@ main() { test("subscription stream after two events", () async { var stream = createStream(); var skips = 0; - var completer = new Completer(); + var completer = Completer(); StreamSubscription subscription; subscription = stream.listen((value) { ++skips; expect(value, skips); if (skips == 2) { - completer.complete(new SubscriptionStream(subscription)); + completer.complete(SubscriptionStream(subscription)); } }); var subscriptionStream = await completer.future; @@ -38,16 +38,16 @@ main() { test("listening twice fails", () async { var stream = createStream(); var sourceSubscription = stream.listen(null); - var subscriptionStream = new SubscriptionStream(sourceSubscription); + var subscriptionStream = SubscriptionStream(sourceSubscription); var subscription = subscriptionStream.listen(null); expect(() => subscriptionStream.listen(null), throwsA(anything)); await subscription.cancel(); }); test("pause and cancel passed through to original stream", () async { - var controller = new StreamController(onCancel: () async => 42); + var controller = StreamController(onCancel: () async => 42); var sourceSubscription = controller.stream.listen(null); - var subscriptionStream = new SubscriptionStream(sourceSubscription); + var subscriptionStream = SubscriptionStream(sourceSubscription); expect(controller.isPaused, isTrue); dynamic lastEvent; var subscription = subscriptionStream.listen((value) { @@ -75,16 +75,16 @@ main() { SubscriptionStream subscriptionStream; Future onCancel; // Completes if source stream is canceled before done. setUp(() { - var cancelCompleter = new Completer(); + var cancelCompleter = Completer(); var source = createErrorStream(cancelCompleter); onCancel = cancelCompleter.future; var sourceSubscription = source.listen(null, cancelOnError: sourceCancels); - subscriptionStream = new SubscriptionStream(sourceSubscription); + subscriptionStream = SubscriptionStream(sourceSubscription); }); test("- subscriptionStream: no", () async { - var done = new Completer(); + var done = Completer(); var events = []; subscriptionStream.listen(events.add, onError: events.add, onDone: done.complete, cancelOnError: false); @@ -96,7 +96,7 @@ main() { done.future.then((_) { isDone = true; }); - await new Future.delayed(const Duration(milliseconds: 5)); + await Future.delayed(const Duration(milliseconds: 5)); expect(isDone, false); } else { expected.add(4); @@ -106,7 +106,7 @@ main() { }); test("- subscriptionStream: yes", () async { - var completer = new Completer(); + var completer = Completer(); var events = []; subscriptionStream.listen(events.add, onError: (value) { @@ -128,7 +128,7 @@ main() { var stream = createStream(); var sourceSubscription = stream.listen(null, cancelOnError: cancelOnError); - var subscriptionStream = new SubscriptionStream(sourceSubscription); + var subscriptionStream = SubscriptionStream(sourceSubscription); var subscription = subscriptionStream.listen(null, cancelOnError: cancelOnError); expect(subscription.asFuture(42), completion(42)); @@ -138,7 +138,7 @@ main() { var stream = createErrorStream(); var sourceSubscription = stream.listen(null, cancelOnError: cancelOnError); - var subscriptionStream = new SubscriptionStream(sourceSubscription); + var subscriptionStream = SubscriptionStream(sourceSubscription); var subscription = subscriptionStream.listen(null, cancelOnError: cancelOnError); @@ -166,7 +166,7 @@ Stream createErrorStream([Completer onCancel]) async* { await flushMicrotasks(); yield 2; await flushMicrotasks(); - yield* new Future.error("To err is divine!").asStream(); + yield* Future.error("To err is divine!").asStream(); await flushMicrotasks(); yield 4; await flushMicrotasks(); diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index dbbf5971..2a709a40 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -13,8 +13,8 @@ void main() { group("with no callbacks", () { test("forwards cancellation", () async { var isCanceled = false; - var cancelCompleter = new Completer(); - var controller = new StreamController(onCancel: expectAsync0(() { + var cancelCompleter = Completer(); + var controller = StreamController(onCancel: expectAsync0(() { isCanceled = true; return cancelCompleter.future; })); @@ -40,7 +40,7 @@ void main() { }); test("forwards pausing and resuming", () async { - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer()) .listen(expectAsync1((_) {}, count: 0)); @@ -63,12 +63,12 @@ void main() { }); test("forwards pausing with a resume future", () async { - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer()) .listen(expectAsync1((_) {}, count: 0)); - var completer = new Completer(); + var completer = Completer(); subscription.pause(completer.future); await flushMicrotasks(); expect(controller.isPaused, isTrue); @@ -83,7 +83,7 @@ void main() { test("invokes the callback when the subscription is canceled", () async { var isCanceled = false; var callbackInvoked = false; - var controller = new StreamController(onCancel: expectAsync0(() { + var controller = StreamController(onCancel: expectAsync0(() { isCanceled = true; })); var subscription = controller.stream.transform( @@ -103,8 +103,8 @@ void main() { }); test("invokes the callback once and caches its result", () async { - var completer = new Completer(); - var controller = new StreamController(); + var completer = Completer(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handleCancel: expectAsync1((inner) => completer.future))) @@ -134,7 +134,7 @@ void main() { group("with a pause callback", () { test("invokes the callback when pause is called", () async { var pauseCount = 0; - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handlePause: expectAsync1((inner) { @@ -166,7 +166,7 @@ void main() { test("doesn't invoke the callback when the subscription has been canceled", () async { - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handlePause: expectAsync1((_) {}, count: 0))) @@ -182,7 +182,7 @@ void main() { group("with a resume callback", () { test("invokes the callback when resume is called", () async { var resumeCount = 0; - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handleResume: expectAsync1((inner) { @@ -214,14 +214,14 @@ void main() { test("invokes the callback when a resume future completes", () async { var resumed = false; - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream.transform( subscriptionTransformer(handleResume: expectAsync1((inner) { resumed = true; inner.resume(); }))).listen(expectAsync1((_) {}, count: 0)); - var completer = new Completer(); + var completer = Completer(); subscription.pause(completer.future); await flushMicrotasks(); expect(resumed, isFalse); @@ -233,7 +233,7 @@ void main() { test("doesn't invoke the callback when the subscription has been canceled", () async { - var controller = new StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handlePause: expectAsync1((_) {}, count: 0))) @@ -249,7 +249,7 @@ void main() { group("when the outer subscription is canceled but the inner is not", () { StreamSubscription subscription; setUp(() { - var controller = new StreamController(); + var controller = StreamController(); subscription = controller.stream .transform(subscriptionTransformer(handleCancel: (_) {})) .listen(expectAsync1((_) {}, count: 0), diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index 50c07b67..bbec8f7e 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -15,11 +15,10 @@ void main() { StreamSubscription wrapper; bool isCanceled; setUp(() { - controller = new StreamController(onCancel: () { + controller = StreamController(onCancel: () { isCanceled = true; }); - wrapper = - new TypeSafeStreamSubscription(controller.stream.listen(null)); + wrapper = TypeSafeStreamSubscription(controller.stream.listen(null)); }); test("onData()", () { @@ -72,11 +71,10 @@ void main() { StreamSubscription wrapper; bool isCanceled; setUp(() { - controller = new StreamController(onCancel: () { + controller = StreamController(onCancel: () { isCanceled = true; }); - wrapper = - new TypeSafeStreamSubscription(controller.stream.listen(null)); + wrapper = TypeSafeStreamSubscription(controller.stream.listen(null)); }); group("throws a CastError for", () { @@ -84,9 +82,9 @@ void main() { expect(() { // TODO(nweiz): Use the wrapper declared in setUp when sdk#26226 is // fixed. - controller = new StreamController(); - wrapper = new TypeSafeStreamSubscription( - controller.stream.listen(null)); + controller = StreamController(); + wrapper = + TypeSafeStreamSubscription(controller.stream.listen(null)); wrapper.onData(expectAsync1((_) {}, count: 0)); controller.add("foo"); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 3f8c0ef9..e517c78a 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -9,9 +9,9 @@ import "package:async/async.dart"; import "package:test/test.dart"; /// A zero-millisecond timer should wait until after all microtasks. -Future flushMicrotasks() => new Future.delayed(Duration.zero); +Future flushMicrotasks() => Future.delayed(Duration.zero); -typedef void OptionalArgAction([a, b]); +typedef OptionalArgAction = void Function([dynamic a, dynamic b]); /// A generic unreachable callback function. /// @@ -38,17 +38,17 @@ Matcher throwsZoned(matcher) => predicate((callback) { /// A matcher that runs a callback in its own zone and asserts that that zone /// emits a [CastError]. -final throwsZonedCastError = throwsZoned(new TypeMatcher()); +final throwsZonedCastError = throwsZoned(TypeMatcher()); /// A matcher that matches a callback or future that throws a [CastError]. -final throwsCastError = throwsA(new TypeMatcher()); +final throwsCastError = throwsA(TypeMatcher()); /// A badly behaved stream which throws if it's ever listened to. /// /// Can be used to test cases where a stream should not be used. class UnusableStream extends Stream { listen(onData, {onError, onDone, cancelOnError}) { - throw new UnimplementedError("Gotcha!"); + throw UnimplementedError("Gotcha!"); } } @@ -58,7 +58,7 @@ class UnusableStream extends Stream { /// The [completer] field allows the user to control the future returned by /// [done] and [close]. class CompleterStreamSink implements StreamSink { - final completer = new Completer(); + final completer = Completer(); Future get done => completer.future; @@ -80,7 +80,7 @@ class TestSink implements StreamSink { var _isClosed = false; Future get done => _doneCompleter.future; - final _doneCompleter = new Completer(); + final _doneCompleter = Completer(); final Function _onDone; @@ -91,22 +91,22 @@ class TestSink implements StreamSink { TestSink({onDone()}) : _onDone = onDone ?? (() {}); void add(T event) { - results.add(new Result.value(event)); + results.add(Result.value(event)); } void addError(error, [StackTrace stackTrace]) { - results.add(new Result.error(error, stackTrace)); + results.add(Result.error(error, stackTrace)); } Future addStream(Stream stream) { - var completer = new Completer.sync(); + var completer = Completer.sync(); stream.listen(add, onError: addError, onDone: completer.complete); return completer.future; } Future close() { _isClosed = true; - _doneCompleter.complete(new Future.microtask(_onDone)); + _doneCompleter.complete(Future.microtask(_onDone)); return done; } } From 9972c2100a4a2f21f40ded9f3f1936df11aebc47 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 12 Mar 2019 21:03:44 -0700 Subject: [PATCH 127/260] Test oldest SDK version on Travis (dart-lang/async#76) --- pkgs/async/.travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index 2ea8538e..dd151e8d 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -2,6 +2,7 @@ language: dart dart: - dev + - 2.0.0 dart_task: - test: --platform vm From 523df1f3d0ec8bbcbd1acc793638ca79f1d2ae6a Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 22 Mar 2019 12:49:42 -0700 Subject: [PATCH 128/260] Add isCanceled/Completed to CancelableOperation (dart-lang/async#79) Closes dart-lang/async#78 --- pkgs/async/CHANGELOG.md | 1 + pkgs/async/lib/src/cancelable_operation.dart | 6 ++++++ pkgs/async/test/cancelable_operation_test.dart | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index c71ac913..3d543da2 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,7 @@ ## 2.1.0 * Fix `CancelableOperation.valueOrCancellation`'s type signature +* Add `isCanceled` and `isCompleted` to `CancelableOperation`. ## 2.0.8 diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index e6115cb5..faf9076c 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -80,6 +80,12 @@ class CancelableOperation { /// This returns the [Future] returned by the [CancelableCompleter]'s /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`. Future cancel() => _completer._cancel(); + + /// Whether this operation has been canceled before it completed. + bool get isCanceled => _completer.isCanceled; + + /// Whether this operation completed before being canceled. + bool get isCompleted => _completer.isCompleted; } /// A completer for a [CancelableOperation]. diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 4a238561..384a023b 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -40,8 +40,10 @@ void main() { test("sends errors in a future to the future", () { expect(completer.operation.value, throwsA("error")); expect(completer.isCompleted, isFalse); + expect(completer.operation.isCompleted, isFalse); completer.complete(Future.error("error")); expect(completer.isCompleted, isTrue); + expect(completer.operation.isCompleted, isTrue); }); test("sends values to valueOrCancellation", () { @@ -122,11 +124,15 @@ void main() { expect(canceled, isFalse); expect(completer.isCanceled, isFalse); + expect(completer.operation.isCanceled, isFalse); expect(completer.isCompleted, isFalse); + expect(completer.operation.isCompleted, isFalse); completer.operation.cancel(); expect(canceled, isTrue); expect(completer.isCanceled, isTrue); + expect(completer.operation.isCanceled, isTrue); expect(completer.isCompleted, isFalse); + expect(completer.operation.isCompleted, isFalse); }); test("returns the onCancel future each time cancel is called", () { From 7bcc6d952ce74e951c390913697c19086d0f3e62 Mon Sep 17 00:00:00 2001 From: Graham Rogers Date: Fri, 5 Apr 2019 06:38:37 +0100 Subject: [PATCH 129/260] Implement CancelableOperation.then (dart-lang/async#83) * Implement CancelableOperation.then * Update version to 2.2.0 --- pkgs/async/CHANGELOG.md | 4 + pkgs/async/lib/src/cancelable_operation.dart | 41 +++++ pkgs/async/pubspec.yaml | 2 +- .../async/test/cancelable_operation_test.dart | 154 ++++++++++++++++++ 4 files changed, 200 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 3d543da2..8436cd47 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.0 + +* Add `then` to `CancelableOperation`. + ## 2.1.0 * Fix `CancelableOperation.valueOrCancellation`'s type signature diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index faf9076c..94601c95 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -75,6 +75,47 @@ class CancelableOperation { return completer.future; } + /// Registers callbacks to be called when this operation completes. + /// + /// [onValue] and [onError] behave in the same way as [Future.then]. + /// + /// If [onCancel] is provided, and this operation is canceled, the [onCancel] + /// callback is called and the returned operation completes with the result. + /// + /// If [onCancel] is not given, and this operation is canceled, then the + /// returned operation is canceled. + /// + /// If [propagateCancel] is `true` and the returned operation is canceled then + /// this operation is canceled. The default is `false`. + CancelableOperation then(FutureOr Function(T) onValue, + {FutureOr Function(Object, StackTrace) onError, + FutureOr Function() onCancel, + bool propagateCancel = false}) { + final completer = + CancelableCompleter(onCancel: propagateCancel ? cancel : null); + + valueOrCancellation().then((T result) { + if (!completer.isCanceled) { + if (isCompleted) { + completer.complete(Future.sync(() => onValue(result))); + } else if (onCancel != null) { + completer.complete(Future.sync(onCancel)); + } else { + completer._cancel(); + } + } + }, onError: (error, stackTrace) { + if (!completer.isCanceled) { + if (onError != null) { + completer.complete(Future.sync(() => onError(error, stackTrace))); + } else { + completer.completeError(error, stackTrace); + } + } + }); + return completer.operation; + } + /// Cancels this operation. /// /// This returns the [Future] returned by the [CancelableCompleter]'s diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0b791785..1b2e96e1 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.1.0 +version: 2.2.0 description: Utility functions and classes related to the 'dart:async' library. author: Dart Team diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 384a023b..a4c93080 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -241,4 +241,158 @@ void main() { expect(completer.isCanceled, isTrue); }); }); + + group("then", () { + FutureOr Function(int) onValue; + FutureOr Function(Object, StackTrace) onError; + FutureOr Function() onCancel; + bool propagateCancel; + CancelableCompleter originalCompleter; + + setUp(() { + // Initialize all functions to ones that expect to not be called. + onValue = expectAsync1((_) {}, count: 0, id: "onValue"); + onError = expectAsync2((e, s) {}, count: 0, id: "onError"); + onCancel = expectAsync0(() {}, count: 0, id: "onCancel"); + propagateCancel = false; + }); + + CancelableOperation runThen() { + originalCompleter = CancelableCompleter(); + return originalCompleter.operation.then(onValue, + onError: onError, + onCancel: onCancel, + propagateCancel: propagateCancel); + } + + group("original operation completes successfully", () { + test("onValue completes successfully", () { + onValue = expectAsync1((v) => v.toString(), count: 1, id: "onValue"); + + expect(runThen().value, completion("1")); + originalCompleter.complete(1); + }); + + test("onValue throws error", () { + // expectAsync1 only works with functions that do not throw. + onValue = (_) => throw "error"; + + expect(runThen().value, throwsA("error")); + originalCompleter.complete(1); + }); + + test("onValue returns Future that throws error", () { + onValue = + expectAsync1((v) => Future.error("error"), count: 1, id: "onValue"); + + expect(runThen().value, throwsA("error")); + originalCompleter.complete(1); + }); + + test("and returned operation is canceled with propagateCancel = false", + () async { + propagateCancel = false; + + runThen().cancel(); + + // onValue should not be called. + originalCompleter.complete(1); + }); + }); + + group("original operation completes with error", () { + test("onError not set", () { + onError = null; + + expect(runThen().value, throwsA("error")); + originalCompleter.completeError("error"); + }); + + test("onError completes successfully", () { + onError = expectAsync2((e, s) => "onError caught $e", + count: 1, id: "onError"); + + expect(runThen().value, completion("onError caught error")); + originalCompleter.completeError("error"); + }); + + test("onError throws", () { + // expectAsync2 does not work with functions that throw. + onError = (e, s) => throw "onError caught $e"; + + expect(runThen().value, throwsA("onError caught error")); + originalCompleter.completeError("error"); + }); + + test("onError returns Future that throws", () { + onError = expectAsync2((e, s) => Future.error("onError caught $e"), + count: 1, id: "onError"); + + expect(runThen().value, throwsA("onError caught error")); + originalCompleter.completeError("error"); + }); + + test("and returned operation is canceled with propagateCancel = false", + () async { + propagateCancel = false; + + runThen().cancel(); + + // onError should not be called. + originalCompleter.completeError("error"); + }); + }); + + group("original operation canceled", () { + test("onCancel not set", () { + onCancel = null; + + final operation = runThen(); + + expect(originalCompleter.operation.cancel(), completes); + expect(operation.isCanceled, true); + }); + + test("onCancel completes successfully", () { + onCancel = expectAsync0(() => "canceled", count: 1, id: "onCancel"); + + expect(runThen().value, completion("canceled")); + originalCompleter.operation.cancel(); + }); + + test("onCancel throws error", () { + // expectAsync0 only works with functions that do not throw. + onCancel = () => throw "error"; + + expect(runThen().value, throwsA("error")); + originalCompleter.operation.cancel(); + }); + + test("onCancel returns Future that throws error", () { + onCancel = + expectAsync0(() => Future.error("error"), count: 1, id: "onCancel"); + + expect(runThen().value, throwsA("error")); + originalCompleter.operation.cancel(); + }); + }); + + group("returned operation canceled", () { + test("propagateCancel is true", () async { + propagateCancel = true; + + await runThen().cancel(); + + expect(originalCompleter.isCanceled, true); + }); + + test("propagateCancel is false", () async { + propagateCancel = false; + + await runThen().cancel(); + + expect(originalCompleter.isCanceled, false); + }); + }); + }); } From 16fc3ddb6272b51a918cbb7b9a9fdbd48b2fbd30 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 1 May 2019 13:55:47 -0700 Subject: [PATCH 130/260] drop unused file --- pkgs/async/codereview.settings | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pkgs/async/codereview.settings diff --git a/pkgs/async/codereview.settings b/pkgs/async/codereview.settings deleted file mode 100644 index af9ef31b..00000000 --- a/pkgs/async/codereview.settings +++ /dev/null @@ -1,3 +0,0 @@ -CODE_REVIEW_SERVER: http://codereview.chromium.org/ -VIEW_VC: https://github.com/dart-lang/async/commit/ -CC_LIST: reviews@dartlang.org From 514d276d61d1d97b670d47a35393dedc94476742 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 1 May 2019 13:56:03 -0700 Subject: [PATCH 131/260] =?UTF-8?q?Drop=20dependencies=20on=20build=5Frunn?= =?UTF-8?q?er,=20etc=20=E2=80=93=20not=20used!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkgs/async/pubspec.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 1b2e96e1..c21ed90f 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -15,7 +15,3 @@ dev_dependencies: fake_async: ^1.0.0 stack_trace: ^1.0.0 test: ^1.0.0 - # For building and testing with DDC - build_runner: ^1.0.0 - build_web_compilers: ^1.0.0 - build_test: ^0.10.1 From aabcb5499b9d81d1ea92f979df24f6347e9738cb Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 1 May 2019 14:00:35 -0700 Subject: [PATCH 132/260] =?UTF-8?q?Enable=20and=20fix=20pedantic=20lints?= =?UTF-8?q?=20-=C2=A0except=20for=20unawaited=5Ffutures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkgs/async/analysis_options.yaml | 6 +++++- pkgs/async/lib/src/stream_splitter.dart | 2 +- pkgs/async/pubspec.yaml | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 82ef545b..f2176979 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,8 +1,12 @@ +include: package:pedantic/analysis_options.yaml analyzer: errors: todo: ignore + # Lint provided by pkg:pedantic – should fix this! + unawaited_futures: ignore linter: rules: + - prefer_generic_function_type_aliases - prefer_typing_uninitialized_variables - - unnecessary_new - unnecessary_const + - unnecessary_new diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index 9ddb63a6..df0ff449 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -125,7 +125,7 @@ class StreamSplitter { assert(_controllers.isEmpty); assert(_isClosed); - var future = null; + Future future; if (_subscription != null) future = _subscription.cancel(); if (future != null) _closeGroup.add(future); _closeGroup.close(); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index c21ed90f..d8ce61ce 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -15,3 +15,4 @@ dev_dependencies: fake_async: ^1.0.0 stack_trace: ^1.0.0 test: ^1.0.0 + pedantic: ^1.0.0 From 9eec8ee3e39b27ca24cf171bff40e4ebe64eca7a Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Thu, 23 May 2019 11:07:41 -0700 Subject: [PATCH 133/260] Fix missing_return violation newly enforced in Dart ~2.3.2-dev.0.1 --- pkgs/async/test/async_cache_test.dart | 6 +++--- pkgs/async/test/cancelable_operation_test.dart | 6 +++--- pkgs/async/test/stream_queue_test.dart | 1 + pkgs/async/test/subscription_transformer_test.dart | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 92e34bfa..5474a379 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -22,7 +22,7 @@ void main() { test('should not fetch via callback when a cache exists', () async { await cache.fetch(() async => 'Expensive'); - expect(await cache.fetch(expectAsync0(() {}, count: 0)), 'Expensive'); + expect(await cache.fetch(expectAsync0(() => null, count: 0)), 'Expensive'); }); test('should not fetch via callback when a future is in-flight', () async { @@ -31,7 +31,7 @@ void main() { var completer = Completer(); expect(cache.fetch(() => completer.future), completion('Expensive')); - expect(cache.fetch(expectAsync0(() {}, count: 0)), completion('Expensive')); + expect(cache.fetch(expectAsync0(() => null, count: 0)), completion('Expensive')); completer.complete('Expensive'); }); @@ -78,7 +78,7 @@ void main() { yield '2'; yield '3'; }).toList(); - expect(await cache.fetchStream(expectAsync0(() {}, count: 0)).toList(), + expect(await cache.fetchStream(expectAsync0(() => null, count: 0)).toList(), ['1', '2', '3']); }); diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index a4c93080..a47e952c 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -251,9 +251,9 @@ void main() { setUp(() { // Initialize all functions to ones that expect to not be called. - onValue = expectAsync1((_) {}, count: 0, id: "onValue"); - onError = expectAsync2((e, s) {}, count: 0, id: "onError"); - onCancel = expectAsync0(() {}, count: 0, id: "onCancel"); + onValue = expectAsync1((_) => null, count: 0, id: "onValue"); + onError = expectAsync2((e, s) => null, count: 0, id: "onError"); + onCancel = expectAsync0(() => null, count: 0, id: "onCancel"); propagateCancel = false; }); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index aaf12270..b2a8625d 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -254,6 +254,7 @@ main() { expect(value, expectedValue); expect(index, sequenceIndex); index++; + return null; }; await Future.wait([ skip1.then(sequence(0, 0)), diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index 2a709a40..64f45ec7 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -90,6 +90,7 @@ void main() { subscriptionTransformer(handleCancel: expectAsync1((inner) { callbackInvoked = true; inner.cancel(); + return null; }))).listen(expectAsync1((_) {}, count: 0)); await flushMicrotasks(); @@ -251,7 +252,7 @@ void main() { setUp(() { var controller = StreamController(); subscription = controller.stream - .transform(subscriptionTransformer(handleCancel: (_) {})) + .transform(subscriptionTransformer(handleCancel: (_) => null)) .listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((_, __) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); From e4b3ce578ce415e644558c72f4cfff4f5eabf453 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Thu, 23 May 2019 11:08:58 -0700 Subject: [PATCH 134/260] Fix missing_return violation newly enforced in Dart ~2.3.2-dev.0.1 --- pkgs/async/test/async_cache_test.dart | 6 +++--- pkgs/async/test/cancelable_operation_test.dart | 6 +++--- pkgs/async/test/stream_queue_test.dart | 1 + pkgs/async/test/subscription_transformer_test.dart | 3 ++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 92e34bfa..5474a379 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -22,7 +22,7 @@ void main() { test('should not fetch via callback when a cache exists', () async { await cache.fetch(() async => 'Expensive'); - expect(await cache.fetch(expectAsync0(() {}, count: 0)), 'Expensive'); + expect(await cache.fetch(expectAsync0(() => null, count: 0)), 'Expensive'); }); test('should not fetch via callback when a future is in-flight', () async { @@ -31,7 +31,7 @@ void main() { var completer = Completer(); expect(cache.fetch(() => completer.future), completion('Expensive')); - expect(cache.fetch(expectAsync0(() {}, count: 0)), completion('Expensive')); + expect(cache.fetch(expectAsync0(() => null, count: 0)), completion('Expensive')); completer.complete('Expensive'); }); @@ -78,7 +78,7 @@ void main() { yield '2'; yield '3'; }).toList(); - expect(await cache.fetchStream(expectAsync0(() {}, count: 0)).toList(), + expect(await cache.fetchStream(expectAsync0(() => null, count: 0)).toList(), ['1', '2', '3']); }); diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index a4c93080..a47e952c 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -251,9 +251,9 @@ void main() { setUp(() { // Initialize all functions to ones that expect to not be called. - onValue = expectAsync1((_) {}, count: 0, id: "onValue"); - onError = expectAsync2((e, s) {}, count: 0, id: "onError"); - onCancel = expectAsync0(() {}, count: 0, id: "onCancel"); + onValue = expectAsync1((_) => null, count: 0, id: "onValue"); + onError = expectAsync2((e, s) => null, count: 0, id: "onError"); + onCancel = expectAsync0(() => null, count: 0, id: "onCancel"); propagateCancel = false; }); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index aaf12270..b2a8625d 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -254,6 +254,7 @@ main() { expect(value, expectedValue); expect(index, sequenceIndex); index++; + return null; }; await Future.wait([ skip1.then(sequence(0, 0)), diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index 2a709a40..64f45ec7 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -90,6 +90,7 @@ void main() { subscriptionTransformer(handleCancel: expectAsync1((inner) { callbackInvoked = true; inner.cancel(); + return null; }))).listen(expectAsync1((_) {}, count: 0)); await flushMicrotasks(); @@ -251,7 +252,7 @@ void main() { setUp(() { var controller = StreamController(); subscription = controller.stream - .transform(subscriptionTransformer(handleCancel: (_) {})) + .transform(subscriptionTransformer(handleCancel: (_) => null)) .listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((_, __) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); From c09f231a574b71fab950bf160404d50e3aeb8a87 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Thu, 23 May 2019 12:47:08 -0700 Subject: [PATCH 135/260] fmt --- pkgs/async/test/async_cache_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 5474a379..b7f7b8e8 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -31,7 +31,8 @@ void main() { var completer = Completer(); expect(cache.fetch(() => completer.future), completion('Expensive')); - expect(cache.fetch(expectAsync0(() => null, count: 0)), completion('Expensive')); + expect(cache.fetch(expectAsync0(() => null, count: 0)), + completion('Expensive')); completer.complete('Expensive'); }); From 9d2a26286a14d0afd726b3c4733909c567a7023d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 23 May 2019 15:39:14 -0700 Subject: [PATCH 136/260] Fix references to dartdocs.org and pub.dartlang.org (dart-lang/async#87) --- pkgs/async/README.md | 48 ++++++++++++++--------------- pkgs/async/lib/src/async_cache.dart | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 60e7b0bf..adbb6535 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -65,29 +65,29 @@ computations. * The [`SubscriptionStream`][SubscriptionStream] class wraps a `StreamSubscription` so it can be re-used as a `Stream`. -[AsyncCache]: https://www.dartdocs.org/documentation/async/latest/async/AsyncCache-class.html -[AsyncMemoizer]: https://www.dartdocs.org/documentation/async/latest/async/AsyncMemoizer-class.html -[CancelableCompleter]: https://www.dartdocs.org/documentation/async/latest/async/CancelableCompleter-class.html -[CancelableOperation]: https://www.dartdocs.org/documentation/async/latest/async/CancelableOperation-class.html -[DelegatingEventSink]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingEventSink-class.html -[DelegatingFuture]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingFuture-class.html -[DelegatingSink]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingSink-class.html -[DelegatingStreamConsumer]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStreamConsumer-class.html -[DelegatingStreamSink]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStreamSink-class.html -[DelegatingStreamSubscription]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStreamSubscription-class.html -[DelegatingStream]: https://www.dartdocs.org/documentation/async/latest/async/DelegatingStream-class.html -[FutureGroup]: https://www.dartdocs.org/documentation/async/latest/async/FutureGroup-class.html -[LazyStream]: https://www.dartdocs.org/documentation/async/latest/async/LazyStream-class.html -[NullStreamSink]: https://www.dartdocs.org/documentation/async/latest/async/NullStreamSink-class.html -[RestartableTimer]: https://www.dartdocs.org/documentation/async/latest/async/RestartableTimer-class.html -[Result]: https://www.dartdocs.org/documentation/async/latest/async/Result-class.html -[SingleSubscriptionTransformer]: https://www.dartdocs.org/documentation/async/latest/async/SingleSubscriptionTransformer-class.html -[StreamGroup]: https://www.dartdocs.org/documentation/async/latest/async/StreamGroup-class.html -[StreamQueue]: https://www.dartdocs.org/documentation/async/latest/async/StreamQueue-class.html -[StreamSinkTransformer]: https://www.dartdocs.org/documentation/async/latest/async/StreamSinkTransformer-class.html +[AsyncCache]: https://pub.dev/documentation/async/latest/async/AsyncCache-class.html +[AsyncMemoizer]: https://pub.dev/documentation/async/latest/async/AsyncMemoizer-class.html +[CancelableCompleter]: https://pub.dev/documentation/async/latest/async/CancelableCompleter-class.html +[CancelableOperation]: https://pub.dev/documentation/async/latest/async/CancelableOperation-class.html +[DelegatingEventSink]: https://pub.dev/documentation/async/latest/async/DelegatingEventSink-class.html +[DelegatingFuture]: https://pub.dev/documentation/async/latest/async/DelegatingFuture-class.html +[DelegatingSink]: https://pub.dev/documentation/async/latest/async/DelegatingSink-class.html +[DelegatingStreamConsumer]: https://pub.dev/documentation/async/latest/async/DelegatingStreamConsumer-class.html +[DelegatingStreamSink]: https://pub.dev/documentation/async/latest/async/DelegatingStreamSink-class.html +[DelegatingStreamSubscription]: https://pub.dev/documentation/async/latest/async/DelegatingStreamSubscription-class.html +[DelegatingStream]: https://pub.dev/documentation/async/latest/async/DelegatingStream-class.html +[FutureGroup]: https://pub.dev/documentation/async/latest/async/FutureGroup-class.html +[LazyStream]: https://pub.dev/documentation/async/latest/async/LazyStream-class.html +[NullStreamSink]: https://pub.dev/documentation/async/latest/async/NullStreamSink-class.html +[RestartableTimer]: https://pub.dev/documentation/async/latest/async/RestartableTimer-class.html +[Result]: https://pub.dev/documentation/async/latest/async/Result-class.html +[SingleSubscriptionTransformer]: https://pub.dev/documentation/async/latest/async/SingleSubscriptionTransformer-class.html +[StreamGroup]: https://pub.dev/documentation/async/latest/async/StreamGroup-class.html +[StreamQueue]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html +[StreamSinkTransformer]: https://pub.dev/documentation/async/latest/async/StreamSinkTransformer-class.html [StreamSink]: https://api.dartlang.org/stable/latest/dart-async/StreamSink-class.html -[StreamSplitter]: https://www.dartdocs.org/documentation/async/latest/async/StreamSplitter-class.html +[StreamSplitter]: https://pub.dev/documentation/async/latest/async/StreamSplitter-class.html [StreamTransformer]: https://api.dartlang.org/stable/latest/dart-async/StreamTransformer-class.html -[StreamZip]: https://www.dartdocs.org/documentation/async/latest/async/StreamZip-class.html -[SubscriptionStream]: https://www.dartdocs.org/documentation/async/latest/async/SubscriptionStream-class.html -[typedStreamTransformer]: https://www.dartdocs.org/documentation/async/latest/async/typedStreamTransformer.html +[StreamZip]: https://pub.dev/documentation/async/latest/async/StreamZip-class.html +[SubscriptionStream]: https://pub.dev/documentation/async/latest/async/SubscriptionStream-class.html +[typedStreamTransformer]: https://pub.dev/documentation/async/latest/async/typedStreamTransformer.html diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 6bd34bd2..4b02304b 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -23,7 +23,7 @@ import 'package:async/async.dart'; /// /// This class's timing can be mocked using [`fake_async`][fake_async]. /// -/// [fake_async]: https://pub.dartlang.org/packages/fake_async +/// [fake_async]: https://pub.dev/packages/fake_async class AsyncCache { /// How long cached values stay fresh. final Duration _duration; From 54bfcffb5170e1cd19c7ad8a29fe99321df7c7b8 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 17 Jul 2019 14:13:17 -0700 Subject: [PATCH 137/260] Implement RestartableTimer.tick (dart-lang/async#89) Closes dart-lang/async#88 Extra cleanup: - change a constructor body to an initializer list - remove an `@override` annotation --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/restartable_timer.dart | 18 ++++++++---------- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 8436cd47..0b7518b3 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.3.0 + +* Implement `RestartableTimer.tick`. + ## 2.2.0 * Add `then` to `CancelableOperation`. diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart index b8e414ad..4720bcdf 100644 --- a/pkgs/async/lib/src/restartable_timer.dart +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -25,9 +25,8 @@ class RestartableTimer implements Timer { /// /// The [callback] function is invoked after the given [duration]. Unlike a /// normal non-periodic [Timer], [callback] may be called more than once. - RestartableTimer(this._duration, this._callback) { - _timer = Timer(_duration, _callback); - } + RestartableTimer(this._duration, this._callback) + : _timer = Timer(_duration, _callback); bool get isActive => _timer.isActive; @@ -44,11 +43,10 @@ class RestartableTimer implements Timer { _timer.cancel(); } - @override - // TODO: Dart 2.0 requires this method to be implemented. - // See https://github.com/dart-lang/sdk/issues/31664 - // ignore: override_on_non_overriding_getter - int get tick { - throw UnimplementedError("tick"); - } + /// The number of durations preceding the most recent timer event on the most + /// recent countdown. + /// + /// Calls to [reset] will also reset the tick so subsequent tick values may + /// not be strictly larger than previous values. + int get tick => _timer.tick; } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index d8ce61ce..b1013ac8 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.2.0 +version: 2.3.0 description: Utility functions and classes related to the 'dart:async' library. author: Dart Team From 583d510e3be0733837542a0f5b6eb68526568b4e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 20 Aug 2019 10:09:53 -0700 Subject: [PATCH 138/260] fix outdated doc links --- pkgs/async/.gitignore | 2 +- pkgs/async/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/async/.gitignore b/pkgs/async/.gitignore index e50bf967..29b55910 100644 --- a/pkgs/async/.gitignore +++ b/pkgs/async/.gitignore @@ -1,4 +1,4 @@ -# See https://www.dartlang.org/guides/libraries/private-files +# See https://dart.dev/guides/libraries/private-files .buildlog .DS_Store diff --git a/pkgs/async/README.md b/pkgs/async/README.md index adbb6535..6e2b8dec 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -85,9 +85,9 @@ computations. [StreamGroup]: https://pub.dev/documentation/async/latest/async/StreamGroup-class.html [StreamQueue]: https://pub.dev/documentation/async/latest/async/StreamQueue-class.html [StreamSinkTransformer]: https://pub.dev/documentation/async/latest/async/StreamSinkTransformer-class.html -[StreamSink]: https://api.dartlang.org/stable/latest/dart-async/StreamSink-class.html +[StreamSink]: https://api.dart.dev/stable/dart-async/StreamSink-class.html [StreamSplitter]: https://pub.dev/documentation/async/latest/async/StreamSplitter-class.html -[StreamTransformer]: https://api.dartlang.org/stable/latest/dart-async/StreamTransformer-class.html +[StreamTransformer]: https://api.dart.dev/stable/dart-async/StreamTransformer-class.html [StreamZip]: https://pub.dev/documentation/async/latest/async/StreamZip-class.html [SubscriptionStream]: https://pub.dev/documentation/async/latest/async/SubscriptionStream-class.html [typedStreamTransformer]: https://pub.dev/documentation/async/latest/async/typedStreamTransformer.html From 9d0d1fc7e218f820b4979e0b3212bbe3d12789f0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 3 Oct 2019 14:33:34 -0700 Subject: [PATCH 139/260] Add StreamGroup.mergeBroadcast (dart-lang/async#90) --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/stream_group.dart | 13 ++++++++++++- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_group_test.dart | 17 +++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 0b7518b3..9071526a 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.0 + +* Add `StreamGroup.mergeBroadcast()` utility. + ## 2.3.0 * Implement `RestartableTimer.tick`. diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index d647ce30..1a6d15dd 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -49,7 +49,7 @@ class StreamGroup implements Sink> { /// re-subscribed. final _subscriptions = Map, StreamSubscription>(); - /// Merges the events from [streams] into a single (single-subscriber) stream. + /// Merges the events from [streams] into a single single-subscription stream. /// /// This is equivalent to adding [streams] to a group, closing that group, and /// returning its stream. @@ -60,6 +60,17 @@ class StreamGroup implements Sink> { return group.stream; } + /// Merges the events from [streams] into a single broadcast stream. + /// + /// This is equivalent to adding [streams] to a broadcast group, closing that + /// group, and returning its stream. + static Stream mergeBroadcast(Iterable> streams) { + var group = StreamGroup.broadcast(); + streams.forEach(group.add); + group.close(); + return group.stream; + } + /// Creates a new stream group where [stream] is single-subscriber. StreamGroup() { _controller = StreamController( diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b1013ac8..aec53cf1 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.3.0 +version: 2.4.0 description: Utility functions and classes related to the 'dart:async' library. author: Dart Team diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 3c4a3a87..96b19db5 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -435,6 +435,23 @@ main() { expect(merged.toList(), completion(unorderedEquals(["first", "second"]))); }); + + test("mergeBroadcast() emits events from all components streams", () { + var controller1 = StreamController(); + var controller2 = StreamController(); + + var merged = + StreamGroup.mergeBroadcast([controller1.stream, controller2.stream]); + + controller1.add("first"); + controller1.close(); + controller2.add("second"); + controller2.close(); + + expect(merged.isBroadcast, isTrue); + + expect(merged.toList(), completion(unorderedEquals(["first", "second"]))); + }); } void regardlessOfType(StreamGroup newStreamGroup()) { From 00e4a9647edea5e4eef8e40dca0c60fc0b8cceea Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 4 Oct 2019 12:20:34 -0700 Subject: [PATCH 140/260] Check ordering in StreamGroup.merege tests (dart-lang/async#91) See https://github.com/dart-lang/async/pull/90#discussion_r331247755 Merged streams forward events synchronously so we do know that guarantees that events keep their order when forwarded. Since it's not necessary to allow for ordering changes drop the `unorderedEquals`. Also refactor to async/await from a `completion` matcher. --- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_group_test.dart | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index aec53cf1..f2439a8b 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.4.0 +version: 2.4.1-dev description: Utility functions and classes related to the 'dart:async' library. author: Dart Team diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 96b19db5..799d9982 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -422,7 +422,7 @@ main() { }); }); - test("merge() emits events from all components streams", () { + test("merge() emits events from all components streams", () async { var controller1 = StreamController(); var controller2 = StreamController(); @@ -433,10 +433,10 @@ main() { controller2.add("second"); controller2.close(); - expect(merged.toList(), completion(unorderedEquals(["first", "second"]))); + expect(await merged.toList(), ["first", "second"]); }); - test("mergeBroadcast() emits events from all components streams", () { + test("mergeBroadcast() emits events from all components streams", () async { var controller1 = StreamController(); var controller2 = StreamController(); @@ -450,7 +450,7 @@ main() { expect(merged.isBroadcast, isTrue); - expect(merged.toList(), completion(unorderedEquals(["first", "second"]))); + expect(await merged.toList(), ["first", "second"]); }); } From 62994835caf8827539ce7ba6b586e1712d28024e Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 5 Dec 2019 16:18:42 -0800 Subject: [PATCH 141/260] Fix newly enforced package:pedantic lints (dart-lang/async#92) - always_declare_return_types - annotate_overrides - curly_braces_in_flow_control_structures - omit_local_variable_types - prefer_collection_literals - prefer_conditional_assignment - prefer_single_quotes - type_init_formals - unnecessary_this - use_function_type_syntax_for_parameters Bump minimum SDK to 2.2.0 for Set literals --- pkgs/async/.travis.yml | 2 +- pkgs/async/lib/async.dart | 60 ++-- pkgs/async/lib/src/async_cache.dart | 16 +- pkgs/async/lib/src/async_memoizer.dart | 2 +- pkgs/async/lib/src/byte_collector.dart | 14 +- pkgs/async/lib/src/cancelable_operation.dart | 8 +- pkgs/async/lib/src/delegate/event_sink.dart | 3 + pkgs/async/lib/src/delegate/future.dart | 13 +- pkgs/async/lib/src/delegate/sink.dart | 2 + .../lib/src/delegate/stream_consumer.dart | 2 + pkgs/async/lib/src/delegate/stream_sink.dart | 5 + .../lib/src/delegate/stream_subscription.dart | 12 +- pkgs/async/lib/src/future_group.dart | 14 +- pkgs/async/lib/src/lazy_stream.dart | 17 +- pkgs/async/lib/src/null_stream_sink.dart | 9 +- pkgs/async/lib/src/restartable_timer.dart | 3 + pkgs/async/lib/src/result/capture_sink.dart | 3 + .../lib/src/result/capture_transformer.dart | 1 + pkgs/async/lib/src/result/error.dart | 9 + pkgs/async/lib/src/result/release_sink.dart | 3 + .../lib/src/result/release_transformer.dart | 1 + pkgs/async/lib/src/result/result.dart | 6 +- pkgs/async/lib/src/result/value.dart | 9 + .../src/single_subscription_transformer.dart | 1 + pkgs/async/lib/src/stream_completer.dart | 11 +- pkgs/async/lib/src/stream_group.dart | 13 +- pkgs/async/lib/src/stream_queue.dart | 43 ++- pkgs/async/lib/src/stream_sink_completer.dart | 9 +- .../lib/src/stream_sink_transformer.dart | 6 +- .../handler_transformer.dart | 7 + .../stream_transformer_wrapper.dart | 6 + .../src/stream_sink_transformer/typed.dart | 1 + pkgs/async/lib/src/stream_splitter.dart | 6 +- .../src/stream_subscription_transformer.dart | 18 +- pkgs/async/lib/src/stream_zip.dart | 25 +- pkgs/async/lib/src/subscription_stream.dart | 10 +- .../lib/src/typed/stream_subscription.dart | 12 +- .../lib/src/typed_stream_transformer.dart | 1 + pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/async_cache_test.dart | 4 +- pkgs/async/test/async_memoizer_test.dart | 28 +- pkgs/async/test/byte_collection_test.dart | 36 +- .../async/test/cancelable_operation_test.dart | 194 +++++------ pkgs/async/test/future_group_test.dart | 30 +- pkgs/async/test/lazy_stream_test.dart | 20 +- pkgs/async/test/null_stream_sink_test.dart | 54 +-- pkgs/async/test/restartable_timer_test.dart | 10 +- .../test/result/result_captureAll_test.dart | 80 ++--- .../test/result/result_flattenAll_test.dart | 27 +- pkgs/async/test/result/result_test.dart | 229 +++++++------ pkgs/async/test/stream_completer_test.dart | 106 +++--- pkgs/async/test/stream_group_test.dart | 250 +++++++------- pkgs/async/test/stream_queue_test.dart | 316 +++++++++--------- .../test/stream_sink_completer_test.dart | 114 +++---- .../test/stream_sink_transformer_test.dart | 40 +-- pkgs/async/test/stream_splitter_test.dart | 28 +- pkgs/async/test/stream_zip_test.dart | 101 +++--- pkgs/async/test/stream_zip_zone_test.dart | 23 +- pkgs/async/test/subscription_stream_test.dart | 48 +-- .../test/subscription_transformer_test.dart | 32 +- .../stream_subscription_test.dart | 54 +-- pkgs/async/test/utils.dart | 27 +- 62 files changed, 1180 insertions(+), 1056 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index dd151e8d..ccc0864d 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -2,7 +2,7 @@ language: dart dart: - dev - - 2.0.0 + - 2.2.0 dart_task: - test: --platform vm diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 25fc0fd1..2af006b0 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -2,33 +2,33 @@ // 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. -export "src/async_cache.dart"; -export "src/async_memoizer.dart"; -export "src/byte_collector.dart"; -export "src/cancelable_operation.dart"; -export "src/delegate/event_sink.dart"; -export "src/delegate/future.dart"; -export "src/delegate/sink.dart"; -export "src/delegate/stream.dart"; -export "src/delegate/stream_consumer.dart"; -export "src/delegate/stream_sink.dart"; -export "src/delegate/stream_subscription.dart"; -export "src/future_group.dart"; -export "src/lazy_stream.dart"; -export "src/null_stream_sink.dart"; -export "src/restartable_timer.dart"; -export "src/result/result.dart"; -export "src/result/error.dart"; -export "src/result/future.dart"; -export "src/result/value.dart"; -export "src/single_subscription_transformer.dart"; -export "src/stream_completer.dart"; -export "src/stream_group.dart"; -export "src/stream_queue.dart"; -export "src/stream_sink_completer.dart"; -export "src/stream_sink_transformer.dart"; -export "src/stream_splitter.dart"; -export "src/stream_subscription_transformer.dart"; -export "src/stream_zip.dart"; -export "src/subscription_stream.dart"; -export "src/typed_stream_transformer.dart"; +export 'src/async_cache.dart'; +export 'src/async_memoizer.dart'; +export 'src/byte_collector.dart'; +export 'src/cancelable_operation.dart'; +export 'src/delegate/event_sink.dart'; +export 'src/delegate/future.dart'; +export 'src/delegate/sink.dart'; +export 'src/delegate/stream.dart'; +export 'src/delegate/stream_consumer.dart'; +export 'src/delegate/stream_sink.dart'; +export 'src/delegate/stream_subscription.dart'; +export 'src/future_group.dart'; +export 'src/lazy_stream.dart'; +export 'src/null_stream_sink.dart'; +export 'src/restartable_timer.dart'; +export 'src/result/result.dart'; +export 'src/result/error.dart'; +export 'src/result/future.dart'; +export 'src/result/value.dart'; +export 'src/single_subscription_transformer.dart'; +export 'src/stream_completer.dart'; +export 'src/stream_group.dart'; +export 'src/stream_queue.dart'; +export 'src/stream_sink_completer.dart'; +export 'src/stream_sink_transformer.dart'; +export 'src/stream_splitter.dart'; +export 'src/stream_subscription_transformer.dart'; +export 'src/stream_zip.dart'; +export 'src/subscription_stream.dart'; +export 'src/typed_stream_transformer.dart'; diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 4b02304b..e0a6f498 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -56,7 +56,7 @@ class AsyncCache { /// /// If [fetch] has been called recently enough, returns its previous return /// value. Otherwise, runs [callback] and returns its new return value. - Future fetch(Future callback()) async { + Future fetch(Future Function() callback) async { if (_cachedStreamSplitter != null) { throw StateError('Previously used to cache via `fetchStream`'); } @@ -74,17 +74,15 @@ class AsyncCache { /// If [fetchStream] has been called recently enough, returns a copy of its /// previous return value. Otherwise, runs [callback] and returns its new /// return value. - Stream fetchStream(Stream callback()) { + Stream fetchStream(Stream Function() callback) { if (_cachedValueFuture != null) { throw StateError('Previously used to cache via `fetch`'); } - if (_cachedStreamSplitter == null) { - _cachedStreamSplitter = StreamSplitter(callback() - .transform(StreamTransformer.fromHandlers(handleDone: (sink) { - _startStaleTimer(); - sink.close(); - }))); - } + _cachedStreamSplitter ??= StreamSplitter( + callback().transform(StreamTransformer.fromHandlers(handleDone: (sink) { + _startStaleTimer(); + sink.close(); + }))); return _cachedStreamSplitter.split(); } diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index 5e834000..7e545fe2 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -39,7 +39,7 @@ class AsyncMemoizer { /// Runs the function, [computation], if it hasn't been run before. /// /// If [runOnce] has already been called, this returns the original result. - Future runOnce(FutureOr computation()) { + Future runOnce(FutureOr Function() computation) { if (!hasRun) _completer.complete(Future.sync(computation)); return future; } diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart index f01cc2ac..0226dea4 100644 --- a/pkgs/async/lib/src/byte_collector.dart +++ b/pkgs/async/lib/src/byte_collector.dart @@ -2,9 +2,9 @@ // 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. -import "dart:async"; -import "dart:typed_data"; -import "cancelable_operation.dart"; +import 'dart:async'; +import 'dart:typed_data'; +import 'cancelable_operation.dart'; /// Collects an asynchronous sequence of byte lists into a single list of bytes. /// @@ -40,10 +40,8 @@ CancelableOperation collectBytesCancelable( /// Performs all the same operations, but the final result is created /// by the [result] function, which has access to the stream subscription /// so it can cancel the operation. -T _collectBytes( - Stream> source, - T result( - StreamSubscription> subscription, Future result)) { +T _collectBytes(Stream> source, + T Function(StreamSubscription>, Future) result) { var byteLists = >[]; var length = 0; var completer = Completer.sync(); @@ -63,7 +61,7 @@ T _collectBytes( // Join a lists of bytes with a known total length into a single [Uint8List]. Uint8List _collect(int length, List> byteLists) { var result = Uint8List(length); - int i = 0; + var i = 0; for (var byteList in byteLists) { var end = i + byteList.length; result.setRange(i, end, byteList); diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 94601c95..bac8f9b2 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -30,7 +30,7 @@ class CancelableOperation { /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. factory CancelableOperation.fromFuture(Future inner, - {FutureOr onCancel()}) { + {FutureOr Function() onCancel}) { var completer = CancelableCompleter(onCancel: onCancel); completer.complete(inner); return completer.operation; @@ -145,7 +145,7 @@ class CancelableCompleter { /// /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. - CancelableCompleter({FutureOr onCancel()}) + CancelableCompleter({FutureOr Function() onCancel}) : _onCancel = onCancel, _inner = Completer() { _operation = CancelableOperation._(this); @@ -171,7 +171,7 @@ class CancelableCompleter { /// If [value] is a [Future], this will complete to the result of that /// [Future] once it completes. void complete([value]) { - if (_isCompleted) throw StateError("Operation already completed"); + if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; if (value is! Future) { @@ -197,7 +197,7 @@ class CancelableCompleter { /// Completes [operation] to [error]. void completeError(Object error, [StackTrace stackTrace]) { - if (_isCompleted) throw StateError("Operation already completed"); + if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; if (_isCanceled) return; diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 95d78413..977ee643 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -25,14 +25,17 @@ class DelegatingEventSink implements EventSink { static EventSink typed(EventSink sink) => sink is EventSink ? sink : DelegatingEventSink._(sink); + @override void add(T data) { _sink.add(data); } + @override void addError(error, [StackTrace stackTrace]) { _sink.addError(error, stackTrace); } + @override void close() { _sink.close(); } diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 62ff96f2..8c5dd043 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -21,16 +21,21 @@ class DelegatingFuture implements Future { static Future typed(Future future) => future is Future ? future : future.then((v) => v as T); + @override Stream asStream() => _future.asStream(); - Future catchError(Function onError, {bool test(Object error)}) => + @override + Future catchError(Function onError, {bool Function(Object error) test}) => _future.catchError(onError, test: test); - Future then(FutureOr onValue(T value), {Function onError}) => + @override + Future then(FutureOr Function(T) onValue, {Function onError}) => _future.then(onValue, onError: onError); - Future whenComplete(action()) => _future.whenComplete(action); + @override + Future whenComplete(FutureOr action) => _future.whenComplete(action); - Future timeout(Duration timeLimit, {onTimeout()}) => + @override + Future timeout(Duration timeLimit, {FutureOr Function() onTimeout}) => _future.timeout(timeLimit, onTimeout: onTimeout); } diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index b8ba76d5..c1e01d71 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -23,10 +23,12 @@ class DelegatingSink implements Sink { static Sink typed(Sink sink) => sink is Sink ? sink : DelegatingSink._(sink); + @override void add(T data) { _sink.add(data); } + @override void close() { _sink.close(); } diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index ad53182e..591f9031 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -27,7 +27,9 @@ class DelegatingStreamConsumer implements StreamConsumer { ? consumer : DelegatingStreamConsumer._(consumer); + @override Future addStream(Stream stream) => _consumer.addStream(stream); + @override Future close() => _consumer.close(); } diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index 67eb33c4..39711896 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -11,6 +11,7 @@ import 'dart:async'; class DelegatingStreamSink implements StreamSink { final StreamSink _sink; + @override Future get done => _sink.done; /// Create delegating sink forwarding calls to [sink]. @@ -27,15 +28,19 @@ class DelegatingStreamSink implements StreamSink { static StreamSink typed(StreamSink sink) => sink is StreamSink ? sink : DelegatingStreamSink._(sink); + @override void add(T data) { _sink.add(data); } + @override void addError(error, [StackTrace stackTrace]) { _sink.addError(error, stackTrace); } + @override Future addStream(Stream stream) => _sink.addStream(stream); + @override Future close() => _sink.close(); } diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 07af08eb..7d258a84 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -28,29 +28,37 @@ class DelegatingStreamSubscription implements StreamSubscription { ? subscription : TypeSafeStreamSubscription(subscription); - void onData(void handleData(T data)) { + @override + void onData(void Function(T) handleData) { _source.onData(handleData); } + @override void onError(Function handleError) { _source.onError(handleError); } - void onDone(void handleDone()) { + @override + void onDone(void Function() handleDone) { _source.onDone(handleDone); } + @override void pause([Future resumeFuture]) { _source.pause(resumeFuture); } + @override void resume() { _source.resume(); } + @override Future cancel() => _source.cancel(); + @override Future asFuture([E futureValue]) => _source.asFuture(futureValue); + @override bool get isPaused => _source.isPaused; } diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 7e174947..a6abff09 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -41,12 +41,8 @@ class FutureGroup implements Sink> { /// /// Once this group isn't waiting on any futures *and* [close] has been /// called, this stream will close. - Stream get onIdle { - if (_onIdleController == null) { - _onIdleController = StreamController.broadcast(sync: true); - } - return _onIdleController.stream; - } + Stream get onIdle => + (_onIdleController ??= StreamController.broadcast(sync: true)).stream; StreamController _onIdleController; @@ -54,11 +50,12 @@ class FutureGroup implements Sink> { /// the order they were added. /// /// The slots for futures that haven't completed yet are `null`. - final _values = List(); + final _values = []; /// Wait for [task] to complete. + @override void add(Future task) { - if (_closed) throw StateError("The FutureGroup is closed."); + if (_closed) throw StateError('The FutureGroup is closed.'); // Ensure that future values are put into [values] in the same order they're // added to the group by pre-allocating a slot for them and recording its @@ -87,6 +84,7 @@ class FutureGroup implements Sink> { /// Signals to the group that the caller is done adding futures, and so /// [future] should fire once all added futures have completed. + @override void close() { _closed = true; if (_pending != 0) return; diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index 278957ad..d2ac33c8 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -2,11 +2,11 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "delegate/stream.dart"; -import "stream_completer.dart"; -import "utils.dart"; +import 'delegate/stream.dart'; +import 'stream_completer.dart'; +import 'utils.dart'; /// A [Stream] wrapper that forwards to another [Stream] that's initialized /// lazily. @@ -20,15 +20,16 @@ class LazyStream extends Stream { /// Creates a single-subscription `Stream` that calls [callback] when it gets /// a listener and forwards to the returned stream. - LazyStream(FutureOr> callback()) : _callback = callback { + LazyStream(FutureOr> Function() callback) : _callback = callback { // Explicitly check for null because we null out [_callback] internally. if (_callback == null) throw ArgumentError.notNull('callback'); } - StreamSubscription listen(void onData(T event), - {Function onError, void onDone(), bool cancelOnError}) { + @override + StreamSubscription listen(void Function(T) onData, + {Function onError, void Function() onDone, bool cancelOnError}) { if (_callback == null) { - throw StateError("Stream has already been listened to."); + throw StateError('Stream has already been listened to.'); } // Null out the callback before we invoke it to ensure that even while diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart index c9d3d5d7..f820328c 100644 --- a/pkgs/async/lib/src/null_stream_sink.dart +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -26,6 +26,7 @@ import 'dart:async'; /// } /// ``` class NullStreamSink implements StreamSink { + @override final Future done; /// Whether the sink has been closed. @@ -54,14 +55,17 @@ class NullStreamSink implements StreamSink { // experiencing an error. ..catchError((_) {}); + @override void add(T data) { _checkEventAllowed(); } + @override void addError(error, [StackTrace stackTrace]) { _checkEventAllowed(); } + @override Future addStream(Stream stream) { _checkEventAllowed(); @@ -75,12 +79,13 @@ class NullStreamSink implements StreamSink { /// Throws a [StateError] if [close] has been called or an [addStream] call is /// pending. void _checkEventAllowed() { - if (_closed) throw StateError("Cannot add to a closed sink."); + if (_closed) throw StateError('Cannot add to a closed sink.'); if (_addingStream) { - throw StateError("Cannot add to a sink while adding a stream."); + throw StateError('Cannot add to a sink while adding a stream.'); } } + @override Future close() { _closed = true; return done; diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart index 4720bcdf..9ecf4431 100644 --- a/pkgs/async/lib/src/restartable_timer.dart +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -28,6 +28,7 @@ class RestartableTimer implements Timer { RestartableTimer(this._duration, this._callback) : _timer = Timer(_duration, _callback); + @override bool get isActive => _timer.isActive; /// Restarts the timer so that it counts down from its original duration @@ -39,6 +40,7 @@ class RestartableTimer implements Timer { _timer = Timer(_duration, _callback); } + @override void cancel() { _timer.cancel(); } @@ -48,5 +50,6 @@ class RestartableTimer implements Timer { /// /// Calls to [reset] will also reset the tick so subsequent tick values may /// not be strictly larger than previous values. + @override int get tick => _timer.tick; } diff --git a/pkgs/async/lib/src/result/capture_sink.dart b/pkgs/async/lib/src/result/capture_sink.dart index 0477a19e..a7c3a592 100644 --- a/pkgs/async/lib/src/result/capture_sink.dart +++ b/pkgs/async/lib/src/result/capture_sink.dart @@ -12,14 +12,17 @@ class CaptureSink implements EventSink { CaptureSink(EventSink> sink) : _sink = sink; + @override void add(T value) { _sink.add(Result.value(value)); } + @override void addError(Object error, [StackTrace stackTrace]) { _sink.add(Result.error(error, stackTrace)); } + @override void close() { _sink.close(); } diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index a727a8e1..1f58d066 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -14,6 +14,7 @@ import 'capture_sink.dart'; class CaptureStreamTransformer extends StreamTransformerBase> { const CaptureStreamTransformer(); + @override Stream> bind(Stream source) => Stream>.eventTransformed( source, (sink) => CaptureSink(sink)); diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index 8f29d130..76e02751 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -15,21 +15,28 @@ class ErrorResult implements Result { /// The stack trace corresponding to where [error] was thrown. final StackTrace stackTrace; + @override bool get isValue => false; + @override bool get isError => true; + @override ValueResult get asValue => null; + @override ErrorResult get asError => this; ErrorResult(this.error, this.stackTrace); + @override void complete(Completer completer) { completer.completeError(error, stackTrace); } + @override void addTo(EventSink sink) { sink.addError(error, stackTrace); } + @override Future get asFuture => Future.error(error, stackTrace); /// Calls an error handler with the error and stacktrace. @@ -46,9 +53,11 @@ class ErrorResult implements Result { } } + @override int get hashCode => error.hashCode ^ stackTrace.hashCode ^ 0x1d61823f; /// This is equal only to an error result with equal [error] and [stackTrace]. + @override bool operator ==(Object other) => other is ErrorResult && error == other.error && diff --git a/pkgs/async/lib/src/result/release_sink.dart b/pkgs/async/lib/src/result/release_sink.dart index a0715a17..78d0f426 100644 --- a/pkgs/async/lib/src/result/release_sink.dart +++ b/pkgs/async/lib/src/result/release_sink.dart @@ -12,16 +12,19 @@ class ReleaseSink implements EventSink> { ReleaseSink(EventSink sink) : _sink = sink; + @override void add(Result result) { result.addTo(_sink); } + @override void addError(Object error, [StackTrace stackTrace]) { // Errors may be added by intermediate processing, even if it is never // added by CaptureSink. _sink.addError(error, stackTrace); } + @override void close() { _sink.close(); } diff --git a/pkgs/async/lib/src/result/release_transformer.dart b/pkgs/async/lib/src/result/release_transformer.dart index 1aead30b..2548ac9b 100644 --- a/pkgs/async/lib/src/result/release_transformer.dart +++ b/pkgs/async/lib/src/result/release_transformer.dart @@ -11,6 +11,7 @@ import 'release_sink.dart'; class ReleaseStreamTransformer extends StreamTransformerBase, T> { const ReleaseStreamTransformer(); + @override Stream bind(Stream> source) { return Stream.eventTransformed(source, _createSink); } diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 28799fae..e04da9f5 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -60,7 +60,7 @@ abstract class Result { /// This generates either a [ValueResult] with the value returned by /// calling `computation`, or an [ErrorResult] with an error thrown by /// the call. - factory Result(T computation()) { + factory Result(T Function() computation) { try { return ValueResult(computation()); } catch (e, s) { @@ -97,11 +97,11 @@ abstract class Result { /// The returned future will never have an error. static Future>> captureAll(Iterable> elements) { var results = >[]; - int pending = 0; + var pending = 0; Completer>> completer; for (var element in elements) { if (element is Future) { - int i = results.length; + var i = results.length; results.add(null); pending++; Result.capture(element).then((result) { diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart index dddc9fe4..5c1a60fc 100644 --- a/pkgs/async/lib/src/result/value.dart +++ b/pkgs/async/lib/src/result/value.dart @@ -12,25 +12,34 @@ class ValueResult implements Result { /// The result of a successful computation. final T value; + @override bool get isValue => true; + @override bool get isError => false; + @override ValueResult get asValue => this; + @override ErrorResult get asError => null; ValueResult(this.value); + @override void complete(Completer completer) { completer.complete(value); } + @override void addTo(EventSink sink) { sink.add(value); } + @override Future get asFuture => Future.value(value); + @override int get hashCode => value.hashCode ^ 0x323f1d61; + @override bool operator ==(Object other) => other is ValueResult && value == other.value; } diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index 8007a42c..fe939fc1 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -16,6 +16,7 @@ import 'dart:async'; class SingleSubscriptionTransformer extends StreamTransformerBase { const SingleSubscriptionTransformer(); + @override Stream bind(Stream stream) { StreamSubscription subscription; var controller = diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index bbb21559..5128d57f 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -2,7 +2,7 @@ // 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. -import "dart:async"; +import 'dart:async'; /// A single-subscription [stream] where the contents are provided later. /// @@ -75,7 +75,7 @@ class StreamCompleter { /// most once. Trying to call any of them again will fail. void setSourceStream(Stream sourceStream) { if (_stream._isSourceStreamSet) { - throw StateError("Source stream already set"); + throw StateError('Source stream already set'); } _stream._setSourceStream(sourceStream); } @@ -86,7 +86,7 @@ class StreamCompleter { /// most once. Trying to call any of them again will fail. void setEmpty() { if (_stream._isSourceStreamSet) { - throw StateError("Source stream already set"); + throw StateError('Source stream already set'); } _stream._setEmpty(); } @@ -116,8 +116,9 @@ class _CompleterStream extends Stream { /// or [_setEmpty]. Stream _sourceStream; - StreamSubscription listen(onData(T data), - {Function onError, void onDone(), bool cancelOnError}) { + @override + StreamSubscription listen(void Function(T) onData, + {Function onError, void Function() onDone, bool cancelOnError}) { if (_controller == null) { if (_sourceStream != null && !_sourceStream.isBroadcast) { // If the source stream is itself single subscription, diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 1a6d15dd..8ea95f3e 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -47,7 +47,7 @@ class StreamGroup implements Sink> { /// subscriptions will be canceled and set to null again. Single-subscriber /// stream subscriptions will be left intact, since they can't be /// re-subscribed. - final _subscriptions = Map, StreamSubscription>(); + final _subscriptions = , StreamSubscription>{}; /// Merges the events from [streams] into a single single-subscription stream. /// @@ -99,6 +99,7 @@ class StreamGroup implements Sink> { /// `null`. /// /// Throws a [StateError] if this group is closed. + @override Future add(Stream stream) { if (_closed) { throw StateError("Can't add a Stream to a closed StreamGroup."); @@ -214,6 +215,7 @@ class StreamGroup implements Sink> { /// Otherwise, [stream] will close once all streams in the group close. /// /// Returns a [Future] that completes once [stream] has actually been closed. + @override Future close() { if (_closed) return _controller.done; @@ -230,12 +232,12 @@ class _StreamGroupState { /// /// New streams added to the group will be listened once the group has a /// listener. - static const dormant = _StreamGroupState("dormant"); + static const dormant = _StreamGroupState('dormant'); /// The group has one or more listeners and is actively firing events. /// /// New streams added to the group will be immediately listeners. - static const listening = _StreamGroupState("listening"); + static const listening = _StreamGroupState('listening'); /// The group is paused and no more events will be fired until it resumes. /// @@ -243,7 +245,7 @@ class _StreamGroupState { /// will be resumed once the group itself is resumed. /// /// This state is only used by single-subscriber groups. - static const paused = _StreamGroupState("paused"); + static const paused = _StreamGroupState('paused'); /// The group is canceled and no more events will be fired ever. /// @@ -251,7 +253,7 @@ class _StreamGroupState { /// discarded. /// /// This state is only used by single-subscriber groups. - static const canceled = _StreamGroupState("canceled"); + static const canceled = _StreamGroupState('canceled'); /// The name of the state. /// @@ -260,5 +262,6 @@ class _StreamGroupState { const _StreamGroupState(this.name); + @override String toString() => name; } diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index fd3186e2..cf75a6d5 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -7,11 +7,11 @@ import 'dart:collection'; import 'package:collection/collection.dart'; -import "cancelable_operation.dart"; -import "result/result.dart"; -import "subscription_stream.dart"; -import "stream_completer.dart"; -import "stream_splitter.dart"; +import 'cancelable_operation.dart'; +import 'result/result.dart'; +import 'subscription_stream.dart'; +import 'stream_completer.dart'; +import 'stream_splitter.dart'; /// An asynchronous pull-based interface for accessing stream events. /// @@ -146,7 +146,7 @@ class StreamQueue { /// If one of the next [count] events is an error, the returned future /// completes with this error, and the error is still left in the queue. Future> lookAhead(int count) { - if (count < 0) throw RangeError.range(count, 0, null, "count"); + if (count < 0) throw RangeError.range(count, 0, null, 'count'); if (!_isClosed) { var request = _LookAheadRequest(count); _addRequest(request); @@ -226,7 +226,7 @@ class StreamQueue { /// then all events were succssfully skipped. If the value /// is greater than zero then the stream ended early. Future skip(int count) { - if (count < 0) throw RangeError.range(count, 0, null, "count"); + if (count < 0) throw RangeError.range(count, 0, null, 'count'); if (!_isClosed) { var request = _SkipRequest(count); _addRequest(request); @@ -251,7 +251,7 @@ class StreamQueue { /// of data collected so far. That is, the returned /// list may have fewer than [count] elements. Future> take(int count) { - if (count < 0) throw RangeError.range(count, 0, null, "count"); + if (count < 0) throw RangeError.range(count, 0, null, 'count'); if (!_isClosed) { var request = _TakeRequest(count); _addRequest(request); @@ -324,7 +324,7 @@ class StreamQueue { /// } /// } /// ``` - Future withTransaction(Future callback(StreamQueue queue)) { + Future withTransaction(Future Function(StreamQueue) callback) { var transaction = startTransaction(); /// Avoid async/await to ensure that [startTransaction] is called @@ -363,7 +363,7 @@ class StreamQueue { /// _stdinQueue.cancelable((queue) => queue.next); /// ``` CancelableOperation cancelable( - Future callback(StreamQueue queue)) { + Future Function(StreamQueue) callback) { var transaction = startTransaction(); var completer = CancelableCompleter(onCancel: () { transaction.reject(); @@ -486,7 +486,7 @@ class StreamQueue { _addResult(Result.error(error, stackTrace)); }, onDone: () { _subscription = null; - this._close(); + _close(); }); } else { _subscription.resume(); @@ -529,7 +529,7 @@ class StreamQueue { /// Returns a [StateError] with a message saying that either /// [cancel] or [rest] have already been called. Error _failClosed() { - return StateError("Already cancelled"); + return StateError('Already cancelled'); } /// Adds a new request to the queue. @@ -558,7 +558,7 @@ class StreamQueueTransaction { final StreamSplitter _splitter; /// Queues created using [newQueue]. - final _queues = Set(); + final _queues = {}; /// Whether [commit] has been called. var _committed = false; @@ -639,9 +639,9 @@ class StreamQueueTransaction { /// Throws a [StateError] if [accept] or [reject] has already been called. void _assertActive() { if (_committed) { - throw StateError("This transaction has already been accepted."); + throw StateError('This transaction has already been accepted.'); } else if (_rejected) { - throw StateError("This transaction has already been rejected."); + throw StateError('This transaction has already been rejected.'); } } } @@ -696,13 +696,14 @@ class _NextRequest implements _EventRequest { Future get future => _completer.future; + @override bool update(QueueList> events, bool isDone) { if (events.isNotEmpty) { events.removeFirst().complete(_completer); return true; } if (isDone) { - _completer.completeError(StateError("No elements"), StackTrace.current); + _completer.completeError(StateError('No elements'), StackTrace.current); return true; } return false; @@ -721,13 +722,14 @@ class _PeekRequest implements _EventRequest { Future get future => _completer.future; + @override bool update(QueueList> events, bool isDone) { if (events.isNotEmpty) { events.first.complete(_completer); return true; } if (isDone) { - _completer.completeError(StateError("No elements"), StackTrace.current); + _completer.completeError(StateError('No elements'), StackTrace.current); return true; } return false; @@ -752,6 +754,7 @@ class _SkipRequest implements _EventRequest { /// The future completed when the correct number of events have been skipped. Future get future => _completer.future; + @override bool update(QueueList> events, bool isDone) { while (_eventsToSkip > 0) { if (events.isEmpty) { @@ -795,6 +798,7 @@ abstract class _ListRequest implements _EventRequest { class _TakeRequest extends _ListRequest { _TakeRequest(int eventsToTake) : super(eventsToTake); + @override bool update(QueueList> events, bool isDone) { while (_list.length < _eventsToTake) { if (events.isEmpty) { @@ -818,6 +822,7 @@ class _TakeRequest extends _ListRequest { class _LookAheadRequest extends _ListRequest { _LookAheadRequest(int eventsToTake) : super(eventsToTake); + @override bool update(QueueList> events, bool isDone) { while (_list.length < _eventsToTake) { if (events.length == _list.length) { @@ -855,6 +860,7 @@ class _CancelRequest implements _EventRequest { /// The future completed when the cancel request is completed. Future get future => _completer.future; + @override bool update(QueueList> events, bool isDone) { if (_streamQueue._isDone) { _completer.complete(); @@ -886,6 +892,7 @@ class _RestRequest implements _EventRequest { /// The stream which will contain the remaining events of [_streamQueue]. Stream get stream => _completer.stream; + @override bool update(QueueList> events, bool isDone) { if (events.isEmpty) { if (_streamQueue._isDone) { @@ -920,6 +927,7 @@ class _HasNextRequest implements _EventRequest { Future get future => _completer.future; + @override bool update(QueueList> events, bool isDone) { if (events.isNotEmpty) { _completer.complete(true); @@ -954,6 +962,7 @@ class _TransactionRequest implements _EventRequest { _transaction = StreamQueueTransaction._(parent, _controller.stream); } + @override bool update(QueueList> events, bool isDone) { while (_eventsSent < events.length) { events[_eventsSent++].addTo(_controller); diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index c7fdd03d..3b315762 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -59,7 +59,7 @@ class StreamSinkCompleter { /// Trying to call either of them again will fail. void setDestinationSink(StreamSink destinationSink) { if (_sink._destinationSink != null) { - throw StateError("Destination sink already set"); + throw StateError('Destination sink already set'); } _sink._setDestinationSink(destinationSink); } @@ -98,6 +98,7 @@ class _CompleterSink implements StreamSink { /// to going through [_controller]. bool get _canSendDirectly => _controller == null && _destinationSink != null; + @override Future get done { if (_doneCompleter != null) return _doneCompleter.future; if (_destinationSink == null) { @@ -107,6 +108,7 @@ class _CompleterSink implements StreamSink { return _destinationSink.done; } + @override void add(T event) { if (_canSendDirectly) { _destinationSink.add(event); @@ -116,6 +118,7 @@ class _CompleterSink implements StreamSink { } } + @override void addError(error, [StackTrace stackTrace]) { if (_canSendDirectly) { _destinationSink.addError(error, stackTrace); @@ -125,6 +128,7 @@ class _CompleterSink implements StreamSink { } } + @override Future addStream(Stream stream) { if (_canSendDirectly) return _destinationSink.addStream(stream); @@ -132,6 +136,7 @@ class _CompleterSink implements StreamSink { return _controller.addStream(stream, cancelOnError: false); } + @override Future close() { if (_canSendDirectly) { _destinationSink.close(); @@ -144,7 +149,7 @@ class _CompleterSink implements StreamSink { /// Create [_controller] if it doesn't yet exist. void _ensureController() { - if (_controller == null) _controller = StreamController(sync: true); + _controller ??= StreamController(sync: true); } /// Sets the destination sink to which events from this sink will be provided. diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index b01132ed..c50cf17d 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -34,9 +34,9 @@ abstract class StreamSinkTransformer { /// they're passed are forwarded to the inner sink. If a handler is omitted, /// the event is passed through unaltered. factory StreamSinkTransformer.fromHandlers( - {void handleData(S data, EventSink sink), - void handleError(Object error, StackTrace stackTrace, EventSink sink), - void handleDone(EventSink sink)}) { + {void Function(S, EventSink) handleData, + void Function(Object, StackTrace, EventSink) handleError, + void Function(EventSink) handleDone}) { return HandlerTransformer(handleData, handleError, handleDone); } diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index 31370035..8d37ce3f 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -30,6 +30,7 @@ class HandlerTransformer implements StreamSinkTransformer { HandlerTransformer(this._handleData, this._handleError, this._handleDone); + @override StreamSink bind(StreamSink sink) => _HandlerSink(this, sink); } @@ -45,12 +46,14 @@ class _HandlerSink implements StreamSink { /// errors. final StreamSink _safeCloseInner; + @override Future get done => _inner.done; _HandlerSink(this._transformer, StreamSink inner) : _inner = inner, _safeCloseInner = _SafeCloseSink(inner); + @override void add(S event) { if (_transformer._handleData == null) { _inner.add(event as T); @@ -59,6 +62,7 @@ class _HandlerSink implements StreamSink { } } + @override void addError(error, [StackTrace stackTrace]) { if (_transformer._handleError == null) { _inner.addError(error, stackTrace); @@ -67,6 +71,7 @@ class _HandlerSink implements StreamSink { } } + @override Future addStream(Stream stream) { return _inner.addStream(stream.transform( StreamTransformer.fromHandlers( @@ -75,6 +80,7 @@ class _HandlerSink implements StreamSink { handleDone: _closeSink))); } + @override Future close() { if (_transformer._handleDone == null) return _inner.close(); @@ -91,6 +97,7 @@ class _HandlerSink implements StreamSink { class _SafeCloseSink extends DelegatingStreamSink { _SafeCloseSink(StreamSink inner) : super(inner); + @override Future close() => super.close().catchError((_) {}); } diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart index 1010f9fe..1df7e5af 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -13,6 +13,7 @@ class StreamTransformerWrapper implements StreamSinkTransformer { const StreamTransformerWrapper(this._transformer); + @override StreamSink bind(StreamSink sink) => _StreamTransformerWrapperSink(_transformer, sink); } @@ -28,6 +29,7 @@ class _StreamTransformerWrapperSink implements StreamSink { /// The original sink that's being transformed. final StreamSink _inner; + @override Future get done => _inner.done; _StreamTransformerWrapperSink( @@ -42,16 +44,20 @@ class _StreamTransformerWrapperSink implements StreamSink { }); } + @override void add(S event) { _controller.add(event); } + @override void addError(error, [StackTrace stackTrace]) { _controller.addError(error, stackTrace); } + @override Future addStream(Stream stream) => _controller.addStream(stream); + @override Future close() { _controller.close(); return _inner.done; diff --git a/pkgs/async/lib/src/stream_sink_transformer/typed.dart b/pkgs/async/lib/src/stream_sink_transformer/typed.dart index 303bc08d..c27bd240 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/typed.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/typed.dart @@ -15,6 +15,7 @@ class TypeSafeStreamSinkTransformer TypeSafeStreamSinkTransformer(this._inner); + @override StreamSink bind(StreamSink sink) => DelegatingStreamSink.typed(_inner.bind(sink)); } diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index df0ff449..e9f326e2 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -32,14 +32,14 @@ class StreamSplitter { /// The buffer of events or errors that have already been emitted by /// [_stream]. - final _buffer = List>(); + final _buffer = >[]; /// The controllers for branches that are listening for future events from /// [_stream]. /// /// Once a branch is canceled, it's removed from this list. When [_stream] is /// done, all branches are removed. - final _controllers = Set>(); + final _controllers = >{}; /// A group of futures returned by [close]. /// @@ -58,7 +58,7 @@ class StreamSplitter { /// [count] defaults to 2. This is the same as creating [count] branches and /// then closing the [StreamSplitter]. static List> splitFrom(Stream stream, [int count]) { - if (count == null) count = 2; + count ??= 2; var splitter = StreamSplitter(stream); var streams = List>.generate(count, (_) => splitter.split()); splitter.close(); diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart index bee96a50..0e289722 100644 --- a/pkgs/async/lib/src/stream_subscription_transformer.dart +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -28,9 +28,9 @@ typedef _VoidHandler = void Function(StreamSubscription inner); /// [StreamSubscription]: [handleCancel] must call `cancel()`, [handlePause] /// must call `pause()`, and [handleResume] must call `resume()`. StreamTransformer subscriptionTransformer( - {Future handleCancel(StreamSubscription inner), - void handlePause(StreamSubscription inner), - void handleResume(StreamSubscription inner)}) { + {Future Function(StreamSubscription) handleCancel, + void Function(StreamSubscription) handlePause, + void Function(StreamSubscription) handleResume}) { return StreamTransformer((stream, cancelOnError) { return _TransformedSubscription( stream.listen(null, cancelOnError: cancelOnError), @@ -61,23 +61,28 @@ class _TransformedSubscription implements StreamSubscription { /// The callback to run when [resume] is called. final _VoidHandler _handleResume; + @override bool get isPaused => _inner?.isPaused ?? false; _TransformedSubscription( this._inner, this._handleCancel, this._handlePause, this._handleResume); - void onData(void handleData(T data)) { + @override + void onData(void Function(T) handleData) { _inner?.onData(handleData); } + @override void onError(Function handleError) { _inner?.onError(handleError); } - void onDone(void handleDone()) { + @override + void onDone(void Function() handleDone) { _inner?.onDone(handleDone); } + @override Future cancel() => _cancelMemoizer.runOnce(() { var inner = _inner; _inner.onData(null); @@ -90,17 +95,20 @@ class _TransformedSubscription implements StreamSubscription { }); final _cancelMemoizer = AsyncMemoizer(); + @override void pause([Future resumeFuture]) { if (_cancelMemoizer.hasRun) return; if (resumeFuture != null) resumeFuture.whenComplete(resume); _handlePause(_inner); } + @override void resume() { if (_cancelMemoizer.hasRun) return; _handleResume(_inner); } + @override Future asFuture([E futureValue]) => _inner?.asFuture(futureValue) ?? Completer().future; } diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index ef14c3eb..e319746a 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -2,7 +2,7 @@ // 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. -import "dart:async"; +import 'dart:async'; /// A stream that combines the values of other streams. /// @@ -17,13 +17,14 @@ class StreamZip extends Stream> { StreamZip(Iterable> streams) : _streams = streams; - StreamSubscription> listen(void onData(List data), - {Function onError, void onDone(), bool cancelOnError}) { + @override + StreamSubscription> listen(void Function(List) onData, + {Function onError, void Function() onDone, bool cancelOnError}) { cancelOnError = identical(true, cancelOnError); var subscriptions = >[]; StreamController> controller; List current; - int dataCount = 0; + var dataCount = 0; /// Called for each data from a subscription in [subscriptions]. void handleData(int index, T data) { @@ -33,7 +34,7 @@ class StreamZip extends Stream> { var data = current; current = List(subscriptions.length); dataCount = 0; - for (int i = 0; i < subscriptions.length; i++) { + for (var i = 0; i < subscriptions.length; i++) { if (i != index) subscriptions[i].resume(); } controller.add(data); @@ -54,14 +55,14 @@ class StreamZip extends Stream> { /// Prematurely cancels all subscriptions since we know that we won't /// be needing any more values. void handleErrorCancel(Object error, StackTrace stackTrace) { - for (int i = 0; i < subscriptions.length; i++) { + for (var i = 0; i < subscriptions.length; i++) { subscriptions[i].cancel(); } controller.addError(error, stackTrace); } void handleDone() { - for (int i = 0; i < subscriptions.length; i++) { + for (var i = 0; i < subscriptions.length; i++) { subscriptions[i].cancel(); } controller.close(); @@ -69,7 +70,7 @@ class StreamZip extends Stream> { try { for (var stream in _streams) { - int index = subscriptions.length; + var index = subscriptions.length; subscriptions.add(stream.listen((data) { handleData(index, data); }, @@ -78,7 +79,7 @@ class StreamZip extends Stream> { cancelOnError: cancelOnError)); } } catch (e) { - for (int i = subscriptions.length - 1; i >= 0; i--) { + for (var i = subscriptions.length - 1; i >= 0; i--) { subscriptions[i].cancel(); } rethrow; @@ -87,18 +88,18 @@ class StreamZip extends Stream> { current = List(subscriptions.length); controller = StreamController>(onPause: () { - for (int i = 0; i < subscriptions.length; i++) { + for (var i = 0; i < subscriptions.length; i++) { // This may pause some subscriptions more than once. // These will not be resumed by onResume below, but must wait for the // next round. subscriptions[i].pause(); } }, onResume: () { - for (int i = 0; i < subscriptions.length; i++) { + for (var i = 0; i < subscriptions.length; i++) { subscriptions[i].resume(); } }, onCancel: () { - for (int i = 0; i < subscriptions.length; i++) { + for (var i = 0; i < subscriptions.length; i++) { // Canceling more than once is safe. subscriptions[i].cancel(); } diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 347c3469..b4286193 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import "delegate/stream_subscription.dart"; +import 'delegate/stream_subscription.dart'; /// A [Stream] adapter for a [StreamSubscription]. /// @@ -38,10 +38,11 @@ class SubscriptionStream extends Stream { _source.onDone(null); } - StreamSubscription listen(void onData(T event), - {Function onError, void onDone(), bool cancelOnError}) { + @override + StreamSubscription listen(void Function(T) onData, + {Function onError, void Function() onDone, bool cancelOnError}) { if (_source == null) { - throw StateError("Stream has already been listened to."); + throw StateError('Stream has already been listened to.'); } cancelOnError = (true == cancelOnError); var subscription = _source; @@ -69,6 +70,7 @@ class _CancelOnErrorSubscriptionWrapper _CancelOnErrorSubscriptionWrapper(StreamSubscription subscription) : super(subscription); + @override void onError(Function handleError) { // Cancel when receiving an error. super.onError((error, StackTrace stackTrace) { diff --git a/pkgs/async/lib/src/typed/stream_subscription.dart b/pkgs/async/lib/src/typed/stream_subscription.dart index 0fab0390..d85b7760 100644 --- a/pkgs/async/lib/src/typed/stream_subscription.dart +++ b/pkgs/async/lib/src/typed/stream_subscription.dart @@ -7,31 +7,39 @@ import 'dart:async'; class TypeSafeStreamSubscription implements StreamSubscription { final StreamSubscription _subscription; + @override bool get isPaused => _subscription.isPaused; TypeSafeStreamSubscription(this._subscription); - void onData(void handleData(T data)) { + @override + void onData(void Function(T) handleData) { _subscription.onData((data) => handleData(data as T)); } + @override void onError(Function handleError) { _subscription.onError(handleError); } - void onDone(void handleDone()) { + @override + void onDone(void Function() handleDone) { _subscription.onDone(handleDone); } + @override void pause([Future resumeFuture]) { _subscription.pause(resumeFuture); } + @override void resume() { _subscription.resume(); } + @override Future cancel() => _subscription.cancel(); + @override Future asFuture([E futureValue]) => _subscription.asFuture(futureValue); } diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart index 9750eb42..23665054 100644 --- a/pkgs/async/lib/src/typed_stream_transformer.dart +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -25,6 +25,7 @@ class _TypeSafeStreamTransformer extends StreamTransformerBase { _TypeSafeStreamTransformer(this._inner); + @override Stream bind(Stream stream) => DelegatingStream.typed(_inner.bind(stream)); } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index f2439a8b..e4722039 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -6,7 +6,7 @@ author: Dart Team homepage: https://www.github.com/dart-lang/async environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.2.0 <3.0.0' dependencies: collection: ^1.5.0 diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index b7f7b8e8..4efbb69f 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -39,7 +39,7 @@ void main() { test('should fetch via a callback again when cache expires', () { FakeAsync().run((fakeAsync) async { var timesCalled = 0; - call() async => 'Called ${++timesCalled}'; + Future call() async => 'Called ${++timesCalled}'; expect(await cache.fetch(call), 'Called 1'); expect(await cache.fetch(call), 'Called 1', reason: 'Cache still fresh'); @@ -57,7 +57,7 @@ void main() { test('should fetch via a callback when manually invalidated', () async { var timesCalled = 0; - call() async => 'Called ${++timesCalled}'; + Future call() async => 'Called ${++timesCalled}'; expect(await cache.fetch(call), 'Called 1'); cache.invalidate(); expect(await cache.fetch(call), 'Called 2'); diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart index 785f9a55..982f7c9b 100644 --- a/pkgs/async/test/async_memoizer_test.dart +++ b/pkgs/async/test/async_memoizer_test.dart @@ -5,11 +5,11 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; -main() { +void main() { AsyncMemoizer cache; setUp(() => cache = AsyncMemoizer()); - test("runs the function only the first time runOnce() is called", () async { + test('runs the function only the first time runOnce() is called', () async { var count = 0; expect(await cache.runOnce(() => count++), equals(0)); expect(count, equals(1)); @@ -18,21 +18,21 @@ main() { expect(count, equals(1)); }); - test("forwards the return value from the function", () async { - expect(cache.future, completion(equals("value"))); - expect(cache.runOnce(() => "value"), completion(equals("value"))); - expect(cache.runOnce(() {}), completion(equals("value"))); + test('forwards the return value from the function', () async { + expect(cache.future, completion(equals('value'))); + expect(cache.runOnce(() => 'value'), completion(equals('value'))); + expect(cache.runOnce(() {}), completion(equals('value'))); }); - test("forwards the return value from an async function", () async { - expect(cache.future, completion(equals("value"))); - expect(cache.runOnce(() async => "value"), completion(equals("value"))); - expect(cache.runOnce(() {}), completion(equals("value"))); + test('forwards the return value from an async function', () async { + expect(cache.future, completion(equals('value'))); + expect(cache.runOnce(() async => 'value'), completion(equals('value'))); + expect(cache.runOnce(() {}), completion(equals('value'))); }); - test("forwards the error from an async function", () async { - expect(cache.future, throwsA("error")); - expect(cache.runOnce(() async => throw "error"), throwsA("error")); - expect(cache.runOnce(() {}), throwsA("error")); + test('forwards the error from an async function', () async { + expect(cache.future, throwsA('error')); + expect(cache.runOnce(() async => throw 'error'), throwsA('error')); + expect(cache.runOnce(() {}), throwsA('error')); }); } diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart index 24d218b1..7f2ecc64 100644 --- a/pkgs/async/test/byte_collection_test.dart +++ b/pkgs/async/test/byte_collection_test.dart @@ -2,14 +2,14 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:test/test.dart"; -import "package:async/async.dart"; +import 'package:test/test.dart'; +import 'package:async/async.dart'; void main() { - group("collectBytes", () { - test("simple list and overflow", () { + group('collectBytes', () { + test('simple list and overflow', () { var result = collectBytes(Stream.fromIterable([ [0], [1], @@ -19,25 +19,25 @@ void main() { expect(result, completion([0, 1, 2, 0])); }); - test("no events", () { + test('no events', () { var result = collectBytes(Stream.fromIterable([])); expect(result, completion([])); }); - test("empty events", () { + test('empty events', () { var result = collectBytes(Stream.fromIterable([[], []])); expect(result, completion([])); }); - test("error event", () { + test('error event', () { var result = collectBytes(Stream.fromIterable( - Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); - expect(result, throwsA("badness")); + Iterable.generate(3, (n) => n == 2 ? throw 'badness' : [n]))); + expect(result, throwsA('badness')); }); }); - group("collectBytes", () { - test("simple list and overflow", () { + group('collectBytes', () { + test('simple list and overflow', () { var result = collectBytesCancelable(Stream.fromIterable([ [0], [1], @@ -47,23 +47,23 @@ void main() { expect(result.value, completion([0, 1, 2, 0])); }); - test("no events", () { + test('no events', () { var result = collectBytesCancelable(Stream.fromIterable([])); expect(result.value, completion([])); }); - test("empty events", () { + test('empty events', () { var result = collectBytesCancelable(Stream.fromIterable([[], []])); expect(result.value, completion([])); }); - test("error event", () { + test('error event', () { var result = collectBytesCancelable(Stream.fromIterable( - Iterable.generate(3, (n) => n == 2 ? throw "badness" : [n]))); - expect(result.value, throwsA("badness")); + Iterable.generate(3, (n) => n == 2 ? throw 'badness' : [n]))); + expect(result.value, throwsA('badness')); }); - test("cancelled", () async { + test('cancelled', () async { var sc = StreamController>(); var result = collectBytesCancelable(sc.stream); // Value never completes. diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index a47e952c..b38c0b93 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -10,100 +10,100 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - group("without being canceled", () { + group('without being canceled', () { CancelableCompleter completer; setUp(() { completer = CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); }); - test("sends values to the future", () { + test('sends values to the future', () { expect(completer.operation.value, completion(equals(1))); expect(completer.isCompleted, isFalse); completer.complete(1); expect(completer.isCompleted, isTrue); }); - test("sends errors to the future", () { - expect(completer.operation.value, throwsA("error")); + test('sends errors to the future', () { + expect(completer.operation.value, throwsA('error')); expect(completer.isCompleted, isFalse); - completer.completeError("error"); + completer.completeError('error'); expect(completer.isCompleted, isTrue); }); - test("sends values in a future to the future", () { + test('sends values in a future to the future', () { expect(completer.operation.value, completion(equals(1))); expect(completer.isCompleted, isFalse); completer.complete(Future.value(1)); expect(completer.isCompleted, isTrue); }); - test("sends errors in a future to the future", () { - expect(completer.operation.value, throwsA("error")); + test('sends errors in a future to the future', () { + expect(completer.operation.value, throwsA('error')); expect(completer.isCompleted, isFalse); expect(completer.operation.isCompleted, isFalse); - completer.complete(Future.error("error")); + completer.complete(Future.error('error')); expect(completer.isCompleted, isTrue); expect(completer.operation.isCompleted, isTrue); }); - test("sends values to valueOrCancellation", () { + test('sends values to valueOrCancellation', () { expect(completer.operation.valueOrCancellation(), completion(equals(1))); completer.complete(1); }); - test("sends errors to valueOrCancellation", () { - expect(completer.operation.valueOrCancellation(), throwsA("error")); - completer.completeError("error"); + test('sends errors to valueOrCancellation', () { + expect(completer.operation.valueOrCancellation(), throwsA('error')); + completer.completeError('error'); }); - group("throws a StateError if completed", () { - test("successfully twice", () { + group('throws a StateError if completed', () { + test('successfully twice', () { completer.complete(1); expect(() => completer.complete(1), throwsStateError); }); - test("successfully then unsuccessfully", () { + test('successfully then unsuccessfully', () { completer.complete(1); - expect(() => completer.completeError("error"), throwsStateError); + expect(() => completer.completeError('error'), throwsStateError); }); - test("unsuccessfully twice", () { - expect(completer.operation.value, throwsA("error")); - completer.completeError("error"); - expect(() => completer.completeError("error"), throwsStateError); + test('unsuccessfully twice', () { + expect(completer.operation.value, throwsA('error')); + completer.completeError('error'); + expect(() => completer.completeError('error'), throwsStateError); }); - test("successfully then with a future", () { + test('successfully then with a future', () { completer.complete(1); expect(() => completer.complete(Completer().future), throwsStateError); }); - test("with a future then successfully", () { + test('with a future then successfully', () { completer.complete(Completer().future); expect(() => completer.complete(1), throwsStateError); }); - test("with a future twice", () { + test('with a future twice', () { completer.complete(Completer().future); expect(() => completer.complete(Completer().future), throwsStateError); }); }); - group("CancelableOperation.fromFuture", () { - test("forwards values", () { + group('CancelableOperation.fromFuture', () { + test('forwards values', () { var operation = CancelableOperation.fromFuture(Future.value(1)); expect(operation.value, completion(equals(1))); }); - test("forwards errors", () { - var operation = CancelableOperation.fromFuture(Future.error("error")); - expect(operation.value, throwsA("error")); + test('forwards errors', () { + var operation = CancelableOperation.fromFuture(Future.error('error')); + expect(operation.value, throwsA('error')); }); }); }); - group("when canceled", () { - test("causes the future never to fire", () async { + group('when canceled', () { + test('causes the future never to fire', () async { var completer = CancelableCompleter(); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); completer.operation.cancel(); @@ -114,7 +114,7 @@ void main() { await flushMicrotasks(); }); - test("fires onCancel", () { + test('fires onCancel', () { var canceled = false; CancelableCompleter completer; completer = CancelableCompleter(onCancel: expectAsync0(() { @@ -135,7 +135,7 @@ void main() { expect(completer.operation.isCompleted, isFalse); }); - test("returns the onCancel future each time cancel is called", () { + test('returns the onCancel future each time cancel is called', () { var completer = CancelableCompleter(onCancel: expectAsync0(() { return Future.value(1); })); @@ -158,8 +158,8 @@ void main() { }); test( - "does call onCancel if the completer has completed to an unfired " - "Future", () { + 'does call onCancel if the completer has completed to an unfired ' + 'Future', () { var completer = CancelableCompleter(onCancel: expectAsync0(() {})); completer.complete(Completer().future); expect(completer.operation.cancel(), completes); @@ -167,7 +167,7 @@ void main() { test( "doesn't call onCancel if the completer has completed to a fired " - "Future", () async { + 'Future', () async { var completer = CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); completer.complete(Future.value(1)); @@ -175,7 +175,7 @@ void main() { expect(completer.operation.cancel(), completes); }); - test("can be completed once after being canceled", () async { + test('can be completed once after being canceled', () async { var completer = CancelableCompleter(); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); await completer.operation.cancel(); @@ -183,21 +183,21 @@ void main() { expect(() => completer.complete(1), throwsStateError); }); - test("fires valueOrCancellation with the given value", () { + test('fires valueOrCancellation with the given value', () { var completer = CancelableCompleter(); expect(completer.operation.valueOrCancellation(1), completion(equals(1))); completer.operation.cancel(); }); - test("pipes an error through valueOrCancellation", () { + test('pipes an error through valueOrCancellation', () { var completer = CancelableCompleter(onCancel: () { - throw "error"; + throw 'error'; }); - expect(completer.operation.valueOrCancellation(1), throwsA("error")); + expect(completer.operation.valueOrCancellation(1), throwsA('error')); completer.operation.cancel(); }); - test("valueOrCancellation waits on the onCancel future", () async { + test('valueOrCancellation waits on the onCancel future', () async { var innerCompleter = Completer(); var completer = CancelableCompleter(onCancel: () => innerCompleter.future); @@ -217,22 +217,22 @@ void main() { }); }); - group("asStream()", () { - test("emits a value and then closes", () { + group('asStream()', () { + test('emits a value and then closes', () { var completer = CancelableCompleter(); expect(completer.operation.asStream().toList(), completion(equals([1]))); completer.complete(1); }); - test("emits an error and then closes", () { + test('emits an error and then closes', () { var completer = CancelableCompleter(); var queue = StreamQueue(completer.operation.asStream()); - expect(queue.next, throwsA("error")); + expect(queue.next, throwsA('error')); expect(queue.hasNext, completion(isFalse)); - completer.completeError("error"); + completer.completeError('error'); }); - test("cancels the completer when the subscription is canceled", () { + test('cancels the completer when the subscription is canceled', () { var completer = CancelableCompleter(onCancel: expectAsync0(() {})); var sub = completer.operation.asStream().listen(expectAsync1((_) {}, count: 0)); @@ -242,7 +242,7 @@ void main() { }); }); - group("then", () { + group('then', () { FutureOr Function(int) onValue; FutureOr Function(Object, StackTrace) onError; FutureOr Function() onCancel; @@ -251,9 +251,9 @@ void main() { setUp(() { // Initialize all functions to ones that expect to not be called. - onValue = expectAsync1((_) => null, count: 0, id: "onValue"); - onError = expectAsync2((e, s) => null, count: 0, id: "onError"); - onCancel = expectAsync0(() => null, count: 0, id: "onCancel"); + onValue = expectAsync1((_) => null, count: 0, id: 'onValue'); + onError = expectAsync2((e, s) => null, count: 0, id: 'onError'); + onCancel = expectAsync0(() => null, count: 0, id: 'onCancel'); propagateCancel = false; }); @@ -265,31 +265,31 @@ void main() { propagateCancel: propagateCancel); } - group("original operation completes successfully", () { - test("onValue completes successfully", () { - onValue = expectAsync1((v) => v.toString(), count: 1, id: "onValue"); + group('original operation completes successfully', () { + test('onValue completes successfully', () { + onValue = expectAsync1((v) => v.toString(), count: 1, id: 'onValue'); - expect(runThen().value, completion("1")); + expect(runThen().value, completion('1')); originalCompleter.complete(1); }); - test("onValue throws error", () { + test('onValue throws error', () { // expectAsync1 only works with functions that do not throw. - onValue = (_) => throw "error"; + onValue = (_) => throw 'error'; - expect(runThen().value, throwsA("error")); + expect(runThen().value, throwsA('error')); originalCompleter.complete(1); }); - test("onValue returns Future that throws error", () { + test('onValue returns Future that throws error', () { onValue = - expectAsync1((v) => Future.error("error"), count: 1, id: "onValue"); + expectAsync1((v) => Future.error('error'), count: 1, id: 'onValue'); - expect(runThen().value, throwsA("error")); + expect(runThen().value, throwsA('error')); originalCompleter.complete(1); }); - test("and returned operation is canceled with propagateCancel = false", + test('and returned operation is canceled with propagateCancel = false', () async { propagateCancel = false; @@ -300,51 +300,51 @@ void main() { }); }); - group("original operation completes with error", () { - test("onError not set", () { + group('original operation completes with error', () { + test('onError not set', () { onError = null; - expect(runThen().value, throwsA("error")); - originalCompleter.completeError("error"); + expect(runThen().value, throwsA('error')); + originalCompleter.completeError('error'); }); - test("onError completes successfully", () { - onError = expectAsync2((e, s) => "onError caught $e", - count: 1, id: "onError"); + test('onError completes successfully', () { + onError = expectAsync2((e, s) => 'onError caught $e', + count: 1, id: 'onError'); - expect(runThen().value, completion("onError caught error")); - originalCompleter.completeError("error"); + expect(runThen().value, completion('onError caught error')); + originalCompleter.completeError('error'); }); - test("onError throws", () { + test('onError throws', () { // expectAsync2 does not work with functions that throw. - onError = (e, s) => throw "onError caught $e"; + onError = (e, s) => throw 'onError caught $e'; - expect(runThen().value, throwsA("onError caught error")); - originalCompleter.completeError("error"); + expect(runThen().value, throwsA('onError caught error')); + originalCompleter.completeError('error'); }); - test("onError returns Future that throws", () { - onError = expectAsync2((e, s) => Future.error("onError caught $e"), - count: 1, id: "onError"); + test('onError returns Future that throws', () { + onError = expectAsync2((e, s) => Future.error('onError caught $e'), + count: 1, id: 'onError'); - expect(runThen().value, throwsA("onError caught error")); - originalCompleter.completeError("error"); + expect(runThen().value, throwsA('onError caught error')); + originalCompleter.completeError('error'); }); - test("and returned operation is canceled with propagateCancel = false", + test('and returned operation is canceled with propagateCancel = false', () async { propagateCancel = false; runThen().cancel(); // onError should not be called. - originalCompleter.completeError("error"); + originalCompleter.completeError('error'); }); }); - group("original operation canceled", () { - test("onCancel not set", () { + group('original operation canceled', () { + test('onCancel not set', () { onCancel = null; final operation = runThen(); @@ -353,32 +353,32 @@ void main() { expect(operation.isCanceled, true); }); - test("onCancel completes successfully", () { - onCancel = expectAsync0(() => "canceled", count: 1, id: "onCancel"); + test('onCancel completes successfully', () { + onCancel = expectAsync0(() => 'canceled', count: 1, id: 'onCancel'); - expect(runThen().value, completion("canceled")); + expect(runThen().value, completion('canceled')); originalCompleter.operation.cancel(); }); - test("onCancel throws error", () { + test('onCancel throws error', () { // expectAsync0 only works with functions that do not throw. - onCancel = () => throw "error"; + onCancel = () => throw 'error'; - expect(runThen().value, throwsA("error")); + expect(runThen().value, throwsA('error')); originalCompleter.operation.cancel(); }); - test("onCancel returns Future that throws error", () { + test('onCancel returns Future that throws error', () { onCancel = - expectAsync0(() => Future.error("error"), count: 1, id: "onCancel"); + expectAsync0(() => Future.error('error'), count: 1, id: 'onCancel'); - expect(runThen().value, throwsA("error")); + expect(runThen().value, throwsA('error')); originalCompleter.operation.cancel(); }); }); - group("returned operation canceled", () { - test("propagateCancel is true", () async { + group('returned operation canceled', () { + test('propagateCancel is true', () async { propagateCancel = true; await runThen().cancel(); @@ -386,7 +386,7 @@ void main() { expect(originalCompleter.isCanceled, true); }); - test("propagateCancel is false", () async { + test('propagateCancel is false', () async { propagateCancel = false; await runThen().cancel(); diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index 770a465b..f99d06df 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -15,8 +15,8 @@ void main() { futureGroup = FutureGroup(); }); - group("with no futures", () { - test("never completes if nothing happens", () async { + group('with no futures', () { + test('never completes if nothing happens', () async { var completed = false; futureGroup.future.then((_) => completed = true); @@ -30,8 +30,8 @@ void main() { }); }); - group("with a future that already completed", () { - test("never completes if nothing happens", () async { + group('with a future that already completed', () { + test('never completes if nothing happens', () async { futureGroup.add(Future.value()); await flushMicrotasks(); @@ -57,12 +57,12 @@ void main() { }); test("completes to that future's error, even if it's not closed", () { - futureGroup.add(Future.error("error")); - expect(futureGroup.future, throwsA("error")); + futureGroup.add(Future.error('error')); + expect(futureGroup.future, throwsA('error')); }); }); - test("completes once all contained futures complete", () async { + test('completes once all contained futures complete', () async { var completer1 = Completer(); var completer2 = Completer(); var completer3 = Completer(); @@ -88,7 +88,7 @@ void main() { expect(completed, isTrue); }); - test("completes to the values of the futures in order of addition", () { + test('completes to the values of the futures in order of addition', () { var completer1 = Completer(); var completer2 = Completer(); var completer3 = Completer(); @@ -116,13 +116,13 @@ void main() { futureGroup.add(completer2.future); futureGroup.add(completer3.future); - completer2.completeError("error 2"); - completer1.completeError("error 1"); - expect(futureGroup.future, throwsA("error 2")); + completer2.completeError('error 2'); + completer1.completeError('error 1'); + expect(futureGroup.future, throwsA('error 2')); }); - group("onIdle:", () { - test("emits an event when the last pending future completes", () async { + group('onIdle:', () { + test('emits an event when the last pending future completes', () async { var idle = false; futureGroup.onIdle.listen((_) => idle = true); @@ -154,7 +154,7 @@ void main() { expect(futureGroup.isIdle, isTrue); }); - test("emits an event each time it becomes idle", () async { + test('emits an event each time it becomes idle', () async { var idle = false; futureGroup.onIdle.listen((_) => idle = true); @@ -180,7 +180,7 @@ void main() { expect(futureGroup.isIdle, isTrue); }); - test("emits an event when the group closes", () async { + test('emits an event when the group closes', () async { // It's important that the order of events here stays consistent over // time, since code may rely on it in subtle ways. var idle = false; diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index db1bc94e..2affe5ba 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -2,19 +2,19 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; -main() { - test("disallows a null callback", () { +void main() { + test('disallows a null callback', () { expect(() => LazyStream(null), throwsArgumentError); }); - test("calls the callback when the stream is listened", () async { + test('calls the callback when the stream is listened', () async { var callbackCalled = false; var stream = LazyStream(expectAsync0(() { callbackCalled = true; @@ -28,7 +28,7 @@ main() { expect(callbackCalled, isTrue); }); - test("calls the callback when the stream is listened", () async { + test('calls the callback when the stream is listened', () async { var callbackCalled = false; var stream = LazyStream(expectAsync0(() { callbackCalled = true; @@ -42,7 +42,7 @@ main() { expect(callbackCalled, isTrue); }); - test("forwards to a synchronously-provided stream", () async { + test('forwards to a synchronously-provided stream', () async { var controller = StreamController(); var stream = LazyStream(expectAsync0(() => controller.stream)); @@ -64,7 +64,7 @@ main() { controller.close(); }); - test("forwards to an asynchronously-provided stream", () async { + test('forwards to an asynchronously-provided stream', () async { var controller = StreamController(); var stream = LazyStream(expectAsync0(() async => controller.stream)); diff --git a/pkgs/async/test/null_stream_sink_test.dart b/pkgs/async/test/null_stream_sink_test.dart index 33e2655f..649cbad1 100644 --- a/pkgs/async/test/null_stream_sink_test.dart +++ b/pkgs/async/test/null_stream_sink_test.dart @@ -2,21 +2,21 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; void main() { - group("constructors", () { - test("done defaults to a completed future", () { + group('constructors', () { + test('done defaults to a completed future', () { var sink = NullStreamSink(); expect(sink.done, completes); }); - test("a custom future may be passed to done", () async { + test('a custom future may be passed to done', () async { var completer = Completer(); var sink = NullStreamSink(done: completer.future); @@ -32,30 +32,30 @@ void main() { expect(doneFired, isTrue); }); - test("NullStreamSink.error passes an error to done", () { - var sink = NullStreamSink.error("oh no"); - expect(sink.done, throwsA("oh no")); + test('NullStreamSink.error passes an error to done', () { + var sink = NullStreamSink.error('oh no'); + expect(sink.done, throwsA('oh no')); }); }); - group("events", () { - test("are silently dropped before close", () { + group('events', () { + test('are silently dropped before close', () { var sink = NullStreamSink(); sink.add(1); - sink.addError("oh no"); + sink.addError('oh no'); }); - test("throw StateErrors after close", () { + test('throw StateErrors after close', () { var sink = NullStreamSink(); expect(sink.close(), completes); expect(() => sink.add(1), throwsStateError); - expect(() => sink.addError("oh no"), throwsStateError); + expect(() => sink.addError('oh no'), throwsStateError); expect(() => sink.addStream(Stream.empty()), throwsStateError); }); - group("addStream", () { - test("listens to the stream then cancels immediately", () async { + group('addStream', () { + test('listens to the stream then cancels immediately', () async { var sink = NullStreamSink(); var canceled = false; var controller = StreamController(onCancel: () { @@ -67,7 +67,7 @@ void main() { expect(canceled, isTrue); }); - test("returns the cancel future", () async { + test('returns the cancel future', () async { var completer = Completer(); var sink = NullStreamSink(); var controller = StreamController(onCancel: () => completer.future); @@ -84,30 +84,30 @@ void main() { expect(addStreamFired, isTrue); }); - test("pipes errors from the cancel future through addStream", () async { + test('pipes errors from the cancel future through addStream', () async { var sink = NullStreamSink(); - var controller = StreamController(onCancel: () => throw "oh no"); - expect(sink.addStream(controller.stream), throwsA("oh no")); + var controller = StreamController(onCancel: () => throw 'oh no'); + expect(sink.addStream(controller.stream), throwsA('oh no')); }); - test("causes events to throw StateErrors until the future completes", + test('causes events to throw StateErrors until the future completes', () async { var sink = NullStreamSink(); var future = sink.addStream(Stream.empty()); expect(() => sink.add(1), throwsStateError); - expect(() => sink.addError("oh no"), throwsStateError); + expect(() => sink.addError('oh no'), throwsStateError); expect(() => sink.addStream(Stream.empty()), throwsStateError); await future; sink.add(1); - sink.addError("oh no"); + sink.addError('oh no'); expect(sink.addStream(Stream.empty()), completes); }); }); }); - test("close returns the done future", () { - var sink = NullStreamSink.error("oh no"); - expect(sink.close(), throwsA("oh no")); + test('close returns the done future', () { + var sink = NullStreamSink.error('oh no'); + expect(sink.close(), throwsA('oh no')); }); } diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index c46c87b6..41f52ab9 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -6,8 +6,8 @@ import 'package:async/async.dart'; import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; -main() { - test("runs the callback once the duration has elapsed", () { +void main() { + test('runs the callback once the duration has elapsed', () { FakeAsync().run((async) { var fired = false; RestartableTimer(Duration(seconds: 5), () { @@ -38,7 +38,7 @@ main() { }); }); - test("resets the duration if the timer is reset before it fires", () { + test('resets the duration if the timer is reset before it fires', () { FakeAsync().run((async) { var fired = false; var timer = RestartableTimer(Duration(seconds: 5), () { @@ -57,7 +57,7 @@ main() { }); }); - test("re-runs the callback if the timer is reset after firing", () { + test('re-runs the callback if the timer is reset after firing', () { FakeAsync().run((async) { var fired = 0; var timer = RestartableTimer(Duration(seconds: 5), () { @@ -77,7 +77,7 @@ main() { }); }); - test("runs the callback if the timer is reset after being canceled", () { + test('runs the callback if the timer is reset after being canceled', () { FakeAsync().run((async) { var fired = false; var timer = RestartableTimer(Duration(seconds: 5), () { diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index c7b688f3..ba8305a1 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -2,62 +2,64 @@ // 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. -import "dart:async"; -import "dart:math" show Random; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'dart:async'; +import 'dart:math' show Random; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; final someStack = StackTrace.current; Result res(int n) => Result.value(n); -Result err(n) => ErrorResult("$n", someStack); +Result err(n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of futures. -Iterable> futures(int count, {bool throwWhen(int index)}) sync* { - for (int i = 0; i < count; i++) { +Iterable> futures(int count, + {bool Function(int index) throwWhen}) sync* { + for (var i = 0; i < count; i++) { if (throwWhen != null && throwWhen(i)) { - yield Future.error("$i", someStack); + yield Future.error('$i', someStack); } else { yield Future.value(i); } } } -main() { - test("empty", () async { +void main() { + test('empty', () async { var all = await Result.captureAll(futures(0)); expect(all, []); }); - group("futures only,", () { - test("single", () async { + group('futures only,', () { + test('single', () async { var all = await Result.captureAll(futures(1)); expect(all, [res(0)]); }); - test("multiple", () async { + test('multiple', () async { var all = await Result.captureAll(futures(3)); expect(all, [res(0), res(1), res(2)]); }); - test("error only", () async { + test('error only', () async { var all = await Result.captureAll(futures(1, throwWhen: (_) => true)); expect(all, [err(0)]); }); - test("multiple error only", () async { + test('multiple error only', () async { var all = await Result.captureAll(futures(3, throwWhen: (_) => true)); expect(all, [err(0), err(1), err(2)]); }); - test("mixed error and value", () async { + test('mixed error and value', () async { var all = await Result.captureAll(futures(4, throwWhen: (x) => x.isOdd)); expect(all, [res(0), err(1), res(2), err(3)]); }); - test("completion permutation 1-2-3", () async { + test('completion permutation 1-2-3', () async { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); @@ -66,22 +68,22 @@ main() { await 0; cs[1].complete(2); await 0; - cs[2].completeError("3", someStack); + cs[2].completeError('3', someStack); }); - test("completion permutation 1-3-2", () async { + test('completion permutation 1-3-2', () async { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; cs[0].complete(1); await 0; - cs[2].completeError("3", someStack); + cs[2].completeError('3', someStack); await 0; cs[1].complete(2); }); - test("completion permutation 2-1-3", () async { + test('completion permutation 2-1-3', () async { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); @@ -90,39 +92,39 @@ main() { await 0; cs[0].complete(1); await 0; - cs[2].completeError("3", someStack); + cs[2].completeError('3', someStack); }); - test("completion permutation 2-3-1", () async { + test('completion permutation 2-3-1', () async { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; cs[1].complete(2); await 0; - cs[2].completeError("3", someStack); + cs[2].completeError('3', someStack); await 0; cs[0].complete(1); }); - test("completion permutation 3-1-2", () async { + test('completion permutation 3-1-2', () async { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; - cs[2].completeError("3", someStack); + cs[2].completeError('3', someStack); await 0; cs[0].complete(1); await 0; cs[1].complete(2); }); - test("completion permutation 3-2-1", () async { + test('completion permutation 3-2-1', () async { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); await 0; - cs[2].completeError("3", someStack); + cs[2].completeError('3', someStack); await 0; cs[1].complete(2); await 0; @@ -130,8 +132,8 @@ main() { }); var seed = Random().nextInt(0x100000000); - int n = 25; // max 32, otherwise rnd.nextInt(1< Completer()); var all = Result.captureAll(cs.map((c) => c.future)); var rnd = Random(seed); @@ -144,27 +146,27 @@ main() { var completeFunctions = List.generate(n, (i) { var c = cs[i]; return () => - throws(i) ? c.completeError("$i", someStack) : c.complete(i); + throws(i) ? c.completeError('$i', someStack) : c.complete(i); }); completeFunctions.shuffle(rnd); - for (int i = 0; i < n; i++) { + for (var i = 0; i < n; i++) { await 0; completeFunctions[i](); } }); }); - group("values only,", () { - test("single", () async { + group('values only,', () { + test('single', () async { var all = await Result.captureAll([1]); expect(all, [res(1)]); }); - test("multiple", () async { + test('multiple', () async { var all = await Result.captureAll([1, 2, 3]); expect(all, [res(1), res(2), res(3)]); }); }); - group("mixed futures and values,", () { - test("no error", () async { + group('mixed futures and values,', () { + test('no error', () async { var all = await Result.captureAll(>[ 1, Future(() => 2), @@ -173,12 +175,12 @@ main() { ]); expect(all, [res(1), res(2), res(3), res(4)]); }); - test("error", () async { + test('error', () async { var all = await Result.captureAll(>[ 1, Future(() => 2), 3, - Future(() async => await Future.error("4", someStack)), + Future(() async => await Future.error('4', someStack)), Future.value(5) ]); expect(all, [res(1), res(2), res(3), err(4), res(5)]); diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart index 4a049eba..890b2d02 100644 --- a/pkgs/async/test/result/result_flattenAll_test.dart +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -2,16 +2,17 @@ // 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. -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; final someStack = StackTrace.current; Result res(T n) => Result.value(n); -Result err(n) => ErrorResult("$n", someStack); +Result err(n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of results. -Iterable> results(int count, {bool throwWhen(int index)}) sync* { - for (int i = 0; i < count; i++) { +Iterable> results(int count, + {bool Function(int index) throwWhen}) sync* { + for (var i = 0; i < count; i++) { if (throwWhen != null && throwWhen(i)) { yield err(i); } else { @@ -20,8 +21,8 @@ Iterable> results(int count, {bool throwWhen(int index)}) sync* { } } -main() { - expectAll(result, expectation) { +void main() { + void expectAll(result, expectation) { if (expectation.isError) { expect(result, expectation); } else { @@ -30,24 +31,24 @@ main() { } } - test("empty", () { + test('empty', () { expectAll(Result.flattenAll(results(0)), res([])); }); - test("single value", () { + test('single value', () { expectAll(Result.flattenAll(results(1)), res([0])); }); - test("single error", () { + test('single error', () { expectAll( Result.flattenAll(results(1, throwWhen: (_) => true)), err(0)); }); - test("multiple values", () { + test('multiple values', () { expectAll(Result.flattenAll(results(5)), res([0, 1, 2, 3, 4])); }); - test("multiple errors", () { + test('multiple errors', () { expectAll(Result.flattenAll(results(5, throwWhen: (x) => x.isOdd)), err(1)); // First error is result. }); - test("error last", () { + test('error last', () { expectAll( Result.flattenAll(results(5, throwWhen: (x) => x == 4)), err(4)); }); diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index a33c1ba2..92fc6b08 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -2,83 +2,83 @@ // 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. -import "dart:async"; -import "dart:collection"; +import 'dart:async'; +import 'dart:collection'; -import "package:async/async.dart"; -import "package:stack_trace/stack_trace.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:stack_trace/stack_trace.dart'; +import 'package:test/test.dart'; void main() { var stack = Trace.current(); - test("create result value", () { - Result result = Result.value(42); + test('create result value', () { + var result = Result.value(42); expect(result.isValue, isTrue); expect(result.isError, isFalse); ValueResult value = result.asValue; expect(value.value, equals(42)); }); - test("create result value 2", () { + test('create result value 2', () { Result result = ValueResult(42); expect(result.isValue, isTrue); expect(result.isError, isFalse); - ValueResult value = result.asValue; + var value = result.asValue; expect(value.value, equals(42)); }); - test("create result error", () { - Result result = Result.error("BAD", stack); + test('create result error', () { + var result = Result.error('BAD', stack); expect(result.isValue, isFalse); expect(result.isError, isTrue); - ErrorResult error = result.asError; - expect(error.error, equals("BAD")); + var error = result.asError; + expect(error.error, equals('BAD')); expect(error.stackTrace, same(stack)); }); - test("create result error 2", () { - Result result = ErrorResult("BAD", stack); + test('create result error 2', () { + var result = ErrorResult('BAD', stack); expect(result.isValue, isFalse); expect(result.isError, isTrue); - ErrorResult error = result.asError; - expect(error.error, equals("BAD")); + var error = result.asError; + expect(error.error, equals('BAD')); expect(error.stackTrace, same(stack)); }); - test("create result error no stack", () { - Result result = Result.error("BAD"); + test('create result error no stack', () { + var result = Result.error('BAD'); expect(result.isValue, isFalse); expect(result.isError, isTrue); - ErrorResult error = result.asError; - expect(error.error, equals("BAD")); + var error = result.asError; + expect(error.error, equals('BAD')); expect(error.stackTrace, isNull); }); - test("complete with value", () { + test('complete with value', () { Result result = ValueResult(42); var c = Completer(); c.future.then(expectAsync1((int v) { expect(v, equals(42)); }), onError: (e, s) { - fail("Unexpected error"); + fail('Unexpected error'); }); result.complete(c); }); - test("complete with error", () { - Result result = ErrorResult("BAD", stack); + test('complete with error', () { + Result result = ErrorResult('BAD', stack); var c = Completer(); c.future.then((bool v) { - fail("Unexpected value $v"); + fail('Unexpected value $v'); }, onError: expectAsync2((e, s) { - expect(e, equals("BAD")); + expect(e, equals('BAD')); expect(s, same(stack)); })); result.complete(c); }); - test("add sink value", () { + test('add sink value', () { var result = ValueResult(42); EventSink sink = TestSink(onData: expectAsync1((v) { expect(v, equals(42)); @@ -86,97 +86,95 @@ void main() { result.addTo(sink); }); - test("add sink error", () { - Result result = ErrorResult("BAD", stack); + test('add sink error', () { + Result result = ErrorResult('BAD', stack); EventSink sink = TestSink(onError: expectAsync2((e, s) { - expect(e, equals("BAD")); + expect(e, equals('BAD')); expect(s, same(stack)); })); result.addTo(sink); }); - test("value as future", () { + test('value as future', () { Result result = ValueResult(42); result.asFuture.then(expectAsync1((int v) { expect(v, equals(42)); }), onError: (e, s) { - fail("Unexpected error"); + fail('Unexpected error'); }); }); - test("error as future", () { - Result result = ErrorResult("BAD", stack); + test('error as future', () { + Result result = ErrorResult('BAD', stack); result.asFuture.then((bool v) { - fail("Unexpected value $v"); + fail('Unexpected value $v'); }, onError: expectAsync2((e, s) { - expect(e, equals("BAD")); + expect(e, equals('BAD')); expect(s, same(stack)); })); }); - test("capture future value", () { - Future value = Future.value(42); + test('capture future value', () { + var value = Future.value(42); Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isTrue); expect(result.isError, isFalse); - ValueResult value = result.asValue; + var value = result.asValue; expect(value.value, equals(42)); }), onError: (e, s) { - fail("Unexpected error: $e"); + fail('Unexpected error: $e'); }); }); - test("capture future error", () { - Future value = Future.error("BAD", stack); + test('capture future error', () { + var value = Future.error('BAD', stack); Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isFalse); expect(result.isError, isTrue); - ErrorResult error = result.asError; - expect(error.error, equals("BAD")); + var error = result.asError; + expect(error.error, equals('BAD')); expect(error.stackTrace, same(stack)); }), onError: (e, s) { - fail("Unexpected error: $e"); + fail('Unexpected error: $e'); }); }); - test("release future value", () { - Future> future = - Future>.value(Result.value(42)); + test('release future value', () { + var future = Future>.value(Result.value(42)); Result.release(future).then(expectAsync1((v) { expect(v, equals(42)); }), onError: (e, s) { - fail("Unexpected error: $e"); + fail('Unexpected error: $e'); }); }); - test("release future error", () { + test('release future error', () { // An error in the result is unwrapped and reified by release. - Future> future = - Future>.value(Result.error("BAD", stack)); + var future = Future>.value(Result.error('BAD', stack)); Result.release(future).then((v) { - fail("Unexpected value: $v"); + fail('Unexpected value: $v'); }, onError: expectAsync2((e, s) { - expect(e, equals("BAD")); + expect(e, equals('BAD')); expect(s, same(stack)); })); }); - test("release future real error", () { + test('release future real error', () { // An error in the error lane is passed through by release. - Future> future = Future>.error("BAD", stack); + var future = Future>.error('BAD', stack); Result.release(future).then((v) { - fail("Unexpected value: $v"); + fail('Unexpected value: $v'); }, onError: expectAsync2((e, s) { - expect(e, equals("BAD")); + expect(e, equals('BAD')); expect(s, same(stack)); })); }); - test("capture stream", () { + test('capture stream', () { var c = StreamController(); var stream = Result.captureStream(c.stream); var expectedList = Queue.from( - [Result.value(42), Result.error("BAD", stack), Result.value(37)]); + [Result.value(42), Result.error('BAD', stack), Result.value(37)]); void listener(Result actual) { expect(expectedList.isEmpty, isFalse); expectResult(actual, expectedList.removeFirst()); @@ -185,21 +183,21 @@ void main() { stream.listen(expectAsync1(listener, count: 3), onDone: expectAsync0(() {}), cancelOnError: true); c.add(42); - c.addError("BAD", stack); + c.addError('BAD', stack); c.add(37); c.close(); }); - test("release stream", () { - StreamController> c = StreamController>(); - Stream stream = Result.releaseStream(c.stream); + test('release stream', () { + var c = StreamController>(); + var stream = Result.releaseStream(c.stream); var events = [ Result.value(42), - Result.error("BAD", stack), + Result.error('BAD', stack), Result.value(37) ]; // Expect the data events, and an extra error event. - var expectedList = Queue.from(events)..add(Result.error("BAD2", stack)); + var expectedList = Queue.from(events)..add(Result.error('BAD2', stack)); void dataListener(int v) { expect(expectedList.isEmpty, isFalse); @@ -219,95 +217,93 @@ void main() { stream.listen(expectAsync1(dataListener, count: 2), onError: expectAsync2(errorListener, count: 2), onDone: expectAsync0(() {})); - for (Result result in events) { + for (var result in events) { c.add(result); // Result value or error in data line. } - c.addError("BAD2", stack); // Error in error line. + c.addError('BAD2', stack); // Error in error line. c.close(); }); - test("release stream cancel on error", () { - StreamController> c = StreamController>(); - Stream stream = Result.releaseStream(c.stream); + test('release stream cancel on error', () { + var c = StreamController>(); + var stream = Result.releaseStream(c.stream); stream.listen(expectAsync1((v) { expect(v, equals(42)); }), onError: expectAsync2((e, s) { - expect(e, equals("BAD")); + expect(e, equals('BAD')); expect(s, same(stack)); }), onDone: () { - fail("Unexpected done event"); + fail('Unexpected done event'); }, cancelOnError: true); c.add(Result.value(42)); - c.add(Result.error("BAD", stack)); + c.add(Result.error('BAD', stack)); c.add(Result.value(37)); c.close(); }); - test("flatten error 1", () { - Result error = Result.error("BAD", stack); - Result flattened = - Result.flatten(Result>.error("BAD", stack)); + test('flatten error 1', () { + var error = Result.error('BAD', stack); + var flattened = Result.flatten(Result>.error('BAD', stack)); expectResult(flattened, error); }); - test("flatten error 2", () { - Result error = Result.error("BAD", stack); - Result> result = Result>.value(error); - Result flattened = Result.flatten(result); + test('flatten error 2', () { + var error = Result.error('BAD', stack); + var result = Result>.value(error); + var flattened = Result.flatten(result); expectResult(flattened, error); }); - test("flatten value", () { - Result> result = - Result>.value(Result.value(42)); + test('flatten value', () { + var result = Result>.value(Result.value(42)); expectResult(Result.flatten(result), Result.value(42)); }); - test("handle unary", () { - ErrorResult result = Result.error("error", stack); - bool called = false; + test('handle unary', () { + ErrorResult result = Result.error('error', stack); + var called = false; result.handle((error) { called = true; - expect(error, "error"); + expect(error, 'error'); }); expect(called, isTrue); }); - test("handle binary", () { - ErrorResult result = Result.error("error", stack); - bool called = false; + test('handle binary', () { + ErrorResult result = Result.error('error', stack); + var called = false; result.handle((error, stackTrace) { called = true; - expect(error, "error"); + expect(error, 'error'); expect(stackTrace, same(stack)); }); expect(called, isTrue); }); - test("handle unary and binary", () { - ErrorResult result = Result.error("error", stack); - bool called = false; + test('handle unary and binary', () { + ErrorResult result = Result.error('error', stack); + var called = false; result.handle((error, [stackTrace]) { called = true; - expect(error, "error"); + expect(error, 'error'); expect(stackTrace, same(stack)); }); expect(called, isTrue); }); - test("handle neither unary nor binary", () { - ErrorResult result = Result.error("error", stack); - expect(() => result.handle(() => fail("unreachable")), throwsA(anything)); - expect(() => result.handle((a, b, c) => fail("unreachable")), + test('handle neither unary nor binary', () { + ErrorResult result = Result.error('error', stack); + expect(() => result.handle(() => fail('unreachable')), throwsA(anything)); + expect(() => result.handle((a, b, c) => fail('unreachable')), throwsA(anything)); - expect(() => result.handle((a, b, {c}) => fail("unreachable")), + expect(() => result.handle((a, b, {c}) => fail('unreachable')), throwsA(anything)); - expect(() => result.handle((a, {b}) => fail("unreachable")), + expect(() => result.handle((a, {b}) => fail('unreachable')), throwsA(anything)); - expect(() => result.handle(({a, b}) => fail("unreachable")), + expect(() => result.handle(({a, b}) => fail('unreachable')), throwsA(anything)); expect( - () => result.handle(({a}) => fail("unreachable")), throwsA(anything)); + () => result.handle(({a}) => fail('unreachable')), throwsA(anything)); }); } @@ -323,36 +319,39 @@ void expectResult(Result actual, Result expected) { } class TestSink implements EventSink { - final Function onData; - final Function onError; - final Function onDone; + final void Function(T) onData; + final void Function(dynamic, StackTrace) onError; + final void Function() onDone; TestSink( - {void this.onData(T data) = _nullData, - void this.onError(e, StackTrace s) = _nullError, - void this.onDone() = _nullDone}); + {this.onData = _nullData, + this.onError = _nullError, + this.onDone = _nullDone}); + @override void add(T value) { onData(value); } + @override void addError(error, [StackTrace stack]) { onError(error, stack); } + @override void close() { onDone(); } static void _nullData(value) { - fail("Unexpected sink add: $value"); + fail('Unexpected sink add: $value'); } static void _nullError(e, StackTrace s) { - fail("Unexpected sink addError: $e"); + fail('Unexpected sink addError: $e'); } static void _nullDone() { - fail("Unepxected sink close"); + fail('Unepxected sink close'); } } diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index fb1f4a57..2cf6728a 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -2,21 +2,21 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart" show StreamCompleter; -import "package:test/test.dart"; +import 'package:async/async.dart' show StreamCompleter; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; -main() { - test("a stream is linked before listening", () async { +void main() { + test('a stream is linked before listening', () async { var completer = StreamCompleter(); completer.setSourceStream(createStream()); expect(completer.stream.toList(), completion([1, 2, 3, 4])); }); - test("listened to before a stream is linked", () async { + test('listened to before a stream is linked', () async { var completer = StreamCompleter(); var done = completer.stream.toList(); await flushMicrotasks(); @@ -32,7 +32,7 @@ main() { completer.setSourceStream(UnusableStream()); // Doesn't throw. }); - test("listen and pause before linking stream", () async { + test('listen and pause before linking stream', () async { var controller = StreamCompleter(); var events = []; var subscription = controller.stream.listen(events.add); @@ -55,7 +55,7 @@ main() { expect(events, [1, 2, 3, 4]); }); - test("pause more than once", () async { + test('pause more than once', () async { var completer = StreamCompleter(); var events = []; var subscription = completer.stream.listen(events.add); @@ -64,7 +64,7 @@ main() { subscription.pause(); subscription.pause(); completer.setSourceStream(createStream()); - for (int i = 0; i < 3; i++) { + for (var i = 0; i < 3; i++) { await flushMicrotasks(); expect(events, []); subscription.resume(); @@ -73,7 +73,7 @@ main() { expect(events, [1, 2, 3, 4]); }); - test("cancel new stream before source is done", () async { + test('cancel new stream before source is done', () async { var completer = StreamCompleter(); var lastEvent = -1; var controller = StreamController(); @@ -85,8 +85,8 @@ main() { subscription.cancel(); } }, - onError: unreachable("error"), - onDone: unreachable("done"), + onError: unreachable('error'), + onDone: unreachable('done'), cancelOnError: true); completer.setSourceStream(controller.stream); expect(controller.hasListener, isTrue); @@ -105,20 +105,20 @@ main() { expect(controller.hasListener, isFalse); }); - test("complete with setEmpty before listening", () async { + test('complete with setEmpty before listening', () async { var completer = StreamCompleter(); completer.setEmpty(); var done = Completer(); - completer.stream.listen(unreachable("data"), - onError: unreachable("error"), onDone: done.complete); + completer.stream.listen(unreachable('data'), + onError: unreachable('error'), onDone: done.complete); await done.future; }); - test("complete with setEmpty after listening", () async { + test('complete with setEmpty after listening', () async { var completer = StreamCompleter(); var done = Completer(); - completer.stream.listen(unreachable("data"), - onError: unreachable("error"), onDone: done.complete); + completer.stream.listen(unreachable('data'), + onError: unreachable('error'), onDone: done.complete); completer.setEmpty(); await done.future; }); @@ -138,7 +138,7 @@ main() { await subscription.asFuture(); }); - test("cancelOnError true when listening before linking stream", () async { + test('cancelOnError true when listening before linking stream', () async { var completer = StreamCompleter(); Object lastEvent = -1; var controller = StreamController(); @@ -146,9 +146,9 @@ main() { expect(value, lessThan(3)); lastEvent = value; }, onError: (value) { - expect(value, "3"); + expect(value, '3'); lastEvent = value; - }, onDone: unreachable("done"), cancelOnError: true); + }, onDone: unreachable('done'), cancelOnError: true); completer.setSourceStream(controller.stream); expect(controller.hasListener, isTrue); @@ -164,14 +164,14 @@ main() { await flushMicrotasks(); expect(lastEvent, 2); expect(controller.hasListener, isTrue); - controller.addError("3"); + controller.addError('3'); await flushMicrotasks(); - expect(lastEvent, "3"); + expect(lastEvent, '3'); expect(controller.hasListener, isFalse); }); - test("cancelOnError true when listening after linking stream", () async { + test('cancelOnError true when listening after linking stream', () async { var completer = StreamCompleter(); Object lastEvent = -1; var controller = StreamController(); @@ -183,9 +183,9 @@ main() { expect(value, lessThan(3)); lastEvent = value; }, onError: (value) { - expect(value, "3"); + expect(value, '3'); lastEvent = value; - }, onDone: unreachable("done"), cancelOnError: true); + }, onDone: unreachable('done'), cancelOnError: true); expect(controller.hasListener, isTrue); @@ -197,13 +197,13 @@ main() { await flushMicrotasks(); expect(lastEvent, 2); expect(controller.hasListener, isTrue); - controller.addError("3"); + controller.addError('3'); await flushMicrotasks(); expect(controller.hasListener, isFalse); }); - test("linking a stream after setSourceStream before listen", () async { + test('linking a stream after setSourceStream before listen', () async { var completer = StreamCompleter(); completer.setSourceStream(createStream()); expect(() => completer.setSourceStream(createStream()), throwsStateError); @@ -214,7 +214,7 @@ main() { expect(() => completer.setEmpty(), throwsStateError); }); - test("linking a stream after setSourceStream after listen", () async { + test('linking a stream after setSourceStream after listen', () async { var completer = StreamCompleter(); var list = completer.stream.toList(); completer.setSourceStream(createStream()); @@ -226,7 +226,7 @@ main() { expect(() => completer.setEmpty(), throwsStateError); }); - test("linking a stream after setEmpty before listen", () async { + test('linking a stream after setEmpty before listen', () async { var completer = StreamCompleter(); completer.setEmpty(); expect(() => completer.setSourceStream(createStream()), throwsStateError); @@ -237,7 +237,7 @@ main() { expect(() => completer.setEmpty(), throwsStateError); }); - test("linking a stream after setEmpty() after listen", () async { + test('linking a stream after setEmpty() after listen', () async { var completer = StreamCompleter(); var list = completer.stream.toList(); completer.setEmpty(); @@ -249,7 +249,7 @@ main() { expect(() => completer.setEmpty(), throwsStateError); }); - test("listening more than once after setting stream", () async { + test('listening more than once after setting stream', () async { var completer = StreamCompleter(); completer.setSourceStream(createStream()); var list = completer.stream.toList(); @@ -258,19 +258,19 @@ main() { expect(() => completer.stream.toList(), throwsStateError); }); - test("listening more than once before setting stream", () async { + test('listening more than once before setting stream', () async { var completer = StreamCompleter(); completer.stream.toList(); expect(() => completer.stream.toList(), throwsStateError); }); - test("setting onData etc. before and after setting stream", () async { + test('setting onData etc. before and after setting stream', () async { var completer = StreamCompleter(); var controller = StreamController(); var subscription = completer.stream.listen(null); Object lastEvent = 0; subscription.onData((value) => lastEvent = value); - subscription.onError((value) => lastEvent = "$value"); + subscription.onError((value) => lastEvent = '$value'); subscription.onDone(() => lastEvent = -1); completer.setSourceStream(controller.stream); await flushMicrotasks(); @@ -279,24 +279,24 @@ main() { expect(lastEvent, 1); controller.addError(2); await flushMicrotasks(); - expect(lastEvent, "2"); + expect(lastEvent, '2'); subscription.onData((value) => lastEvent = -value); - subscription.onError((value) => lastEvent = "${-value}"); + subscription.onError((value) => lastEvent = '${-value}'); controller.add(1); await flushMicrotasks(); expect(lastEvent, -1); controller.addError(2); await flushMicrotasks(); - expect(lastEvent, "-2"); + expect(lastEvent, '-2'); controller.close(); await flushMicrotasks(); expect(lastEvent, -1); }); - test("pause w/ resume future accross setting stream", () async { + test('pause w/ resume future accross setting stream', () async { var completer = StreamCompleter(); var resume = Completer(); - var subscription = completer.stream.listen(unreachable("data")); + var subscription = completer.stream.listen(unreachable('data')); subscription.pause(resume.future); await flushMicrotasks(); completer.setSourceStream(createStream()); @@ -308,43 +308,43 @@ main() { expect(events, [1, 2, 3, 4]); }); - test("asFuture with error accross setting stream", () async { + test('asFuture with error accross setting stream', () async { var completer = StreamCompleter(); var controller = StreamController(); var subscription = - completer.stream.listen(unreachable("data"), cancelOnError: false); + completer.stream.listen(unreachable('data'), cancelOnError: false); var done = subscription.asFuture(); expect(controller.hasListener, isFalse); completer.setSourceStream(controller.stream); await flushMicrotasks(); expect(controller.hasListener, isTrue); controller.addError(42); - await done.then(unreachable("data"), onError: (error) { + await done.then(unreachable('data'), onError: (error) { expect(error, 42); }); expect(controller.hasListener, isFalse); }); - group("setError()", () { - test("produces a stream that emits a single error", () { + group('setError()', () { + test('produces a stream that emits a single error', () { var completer = StreamCompleter(); - completer.stream.listen(unreachable("data"), + completer.stream.listen(unreachable('data'), onError: expectAsync2((error, stackTrace) { - expect(error, equals("oh no")); + expect(error, equals('oh no')); }), onDone: expectAsync0(() {})); - completer.setError("oh no"); + completer.setError('oh no'); }); - test("produces a stream that emits a single error on a later listen", + test('produces a stream that emits a single error on a later listen', () async { var completer = StreamCompleter(); - completer.setError("oh no"); + completer.setError('oh no'); await flushMicrotasks(); - completer.stream.listen(unreachable("data"), + completer.stream.listen(unreachable('data'), onError: expectAsync2((error, stackTrace) { - expect(error, equals("oh no")); + expect(error, equals('oh no')); }), onDone: expectAsync0(() {})); }); }); diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 799d9982..690fbc17 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -7,22 +7,22 @@ import 'dart:async'; import 'package:async/async.dart'; import 'package:test/test.dart'; -main() { - group("single-subscription", () { +void main() { + group('single-subscription', () { StreamGroup streamGroup; setUp(() { streamGroup = StreamGroup(); }); - test("buffers events from multiple sources", () async { + test('buffers events from multiple sources', () async { var controller1 = StreamController(); streamGroup.add(controller1.stream); - controller1.add("first"); + controller1.add('first'); controller1.close(); var controller2 = StreamController(); streamGroup.add(controller2.stream); - controller2.add("second"); + controller2.add('second'); controller2.close(); await flushMicrotasks(); @@ -30,18 +30,18 @@ main() { expect(streamGroup.close(), completes); expect(streamGroup.stream.toList(), - completion(unorderedEquals(["first", "second"]))); + completion(unorderedEquals(['first', 'second']))); }); - test("buffers errors from multiple sources", () async { + test('buffers errors from multiple sources', () async { var controller1 = StreamController(); streamGroup.add(controller1.stream); - controller1.addError("first"); + controller1.addError('first'); controller1.close(); var controller2 = StreamController(); streamGroup.add(controller2.stream); - controller2.addError("second"); + controller2.addError('second'); controller2.close(); await flushMicrotasks(); @@ -50,21 +50,21 @@ main() { var transformed = streamGroup.stream.transform( StreamTransformer.fromHandlers( - handleError: (error, _, sink) => sink.add("error: $error"))); + handleError: (error, _, sink) => sink.add('error: $error'))); expect(transformed.toList(), - completion(equals(["error: first", "error: second"]))); + completion(equals(['error: first', 'error: second']))); }); - test("buffers events and errors together", () async { + test('buffers events and errors together', () async { var controller = StreamController(); streamGroup.add(controller.stream); - controller.add("first"); - controller.addError("second"); - controller.add("third"); - controller.addError("fourth"); - controller.addError("fifth"); - controller.add("sixth"); + controller.add('first'); + controller.addError('second'); + controller.add('third'); + controller.addError('fourth'); + controller.addError('fifth'); + controller.add('sixth'); controller.close(); await flushMicrotasks(); @@ -73,17 +73,17 @@ main() { var transformed = streamGroup.stream.transform( StreamTransformer.fromHandlers( - handleData: (data, sink) => sink.add("data: $data"), - handleError: (error, _, sink) => sink.add("error: $error"))); + handleData: (data, sink) => sink.add('data: $data'), + handleError: (error, _, sink) => sink.add('error: $error'))); expect( transformed.toList(), completion(equals([ - "data: first", - "error: second", - "data: third", - "error: fourth", - "error: fifth", - "data: sixth" + 'data: first', + 'error: second', + 'data: third', + 'error: fourth', + 'error: fifth', + 'data: sixth' ]))); }); @@ -92,10 +92,10 @@ main() { streamGroup.add(controller.stream); expect( - streamGroup.stream.toList(), completion(equals(["first", "second"]))); + streamGroup.stream.toList(), completion(equals(['first', 'second']))); - controller.add("first"); - controller.add("second"); + controller.add('first'); + controller.add('second'); controller.close(); expect(streamGroup.close(), completes); @@ -105,8 +105,8 @@ main() { var controller = StreamController.broadcast(); streamGroup.add(controller.stream); - controller.add("first"); - controller.add("second"); + controller.add('first'); + controller.add('second'); controller.close(); await flushMicrotasks(); @@ -115,7 +115,7 @@ main() { expect(streamGroup.stream.toList(), completion(isEmpty)); }); - test("when paused, buffers events from a broadcast stream", () async { + test('when paused, buffers events from a broadcast stream', () async { var controller = StreamController.broadcast(); streamGroup.add(controller.stream); @@ -123,8 +123,8 @@ main() { var subscription = streamGroup.stream.listen(events.add); subscription.pause(); - controller.add("first"); - controller.add("second"); + controller.add('first'); + controller.add('second'); controller.close(); await flushMicrotasks(); @@ -132,7 +132,7 @@ main() { expect(streamGroup.close(), completes); await flushMicrotasks(); - expect(events, equals(["first", "second"])); + expect(events, equals(['first', 'second'])); }); test("emits events from a broadcast stream once there's a listener", () { @@ -140,26 +140,26 @@ main() { streamGroup.add(controller.stream); expect( - streamGroup.stream.toList(), completion(equals(["first", "second"]))); + streamGroup.stream.toList(), completion(equals(['first', 'second']))); - controller.add("first"); - controller.add("second"); + controller.add('first'); + controller.add('second'); controller.close(); expect(streamGroup.close(), completes); }); - test("forwards cancel errors", () async { + test('forwards cancel errors', () async { var subscription = streamGroup.stream.listen(null); - var controller = StreamController(onCancel: () => throw "error"); + var controller = StreamController(onCancel: () => throw 'error'); streamGroup.add(controller.stream); await flushMicrotasks(); - expect(subscription.cancel(), throwsA("error")); + expect(subscription.cancel(), throwsA('error')); }); - test("forwards a cancel future", () async { + test('forwards a cancel future', () async { var subscription = streamGroup.stream.listen(null); var completer = Completer(); @@ -180,8 +180,8 @@ main() { }); test( - "add() while active pauses the stream if the group is paused, then " - "resumes once the group resumes", () async { + 'add() while active pauses the stream if the group is paused, then ' + 'resumes once the group resumes', () async { var subscription = streamGroup.stream.listen(null); await flushMicrotasks(); @@ -201,13 +201,13 @@ main() { expect(paused, isFalse); }); - group("add() while canceled", () { + group('add() while canceled', () { setUp(() async { streamGroup.stream.listen(null).cancel(); await flushMicrotasks(); }); - test("immediately listens to and cancels the stream", () async { + test('immediately listens to and cancels the stream', () async { var listened = false; var canceled = false; var controller = StreamController(onListen: () { @@ -223,14 +223,14 @@ main() { expect(canceled, isTrue); }); - test("forwards cancel errors", () { + test('forwards cancel errors', () { var controller = - StreamController(onCancel: () => throw "error"); + StreamController(onCancel: () => throw 'error'); - expect(streamGroup.add(controller.stream), throwsA("error")); + expect(streamGroup.add(controller.stream), throwsA('error')); }); - test("forwards a cancel future", () async { + test('forwards a cancel future', () async { var completer = Completer(); var controller = StreamController(onCancel: () => completer.future); @@ -248,21 +248,21 @@ main() { }); }); - group("broadcast", () { + group('broadcast', () { StreamGroup streamGroup; setUp(() { streamGroup = StreamGroup.broadcast(); }); - test("buffers events from multiple sources", () async { + test('buffers events from multiple sources', () async { var controller1 = StreamController(); streamGroup.add(controller1.stream); - controller1.add("first"); + controller1.add('first'); controller1.close(); var controller2 = StreamController(); streamGroup.add(controller2.stream); - controller2.add("second"); + controller2.add('second'); controller2.close(); await flushMicrotasks(); @@ -270,7 +270,7 @@ main() { expect(streamGroup.close(), completes); expect( - streamGroup.stream.toList(), completion(equals(["first", "second"]))); + streamGroup.stream.toList(), completion(equals(['first', 'second']))); }); test("emits events from multiple sources once there's a listener", () { @@ -281,10 +281,10 @@ main() { streamGroup.add(controller2.stream); expect( - streamGroup.stream.toList(), completion(equals(["first", "second"]))); + streamGroup.stream.toList(), completion(equals(['first', 'second']))); - controller1.add("first"); - controller2.add("second"); + controller1.add('first'); + controller2.add('second'); controller1.close(); controller2.close(); @@ -299,8 +299,8 @@ main() { streamGroup.stream.listen(null).cancel(); await flushMicrotasks(); - controller.add("first"); - controller.addError("second"); + controller.add('first'); + controller.addError('second'); controller.close(); await flushMicrotasks(); @@ -312,8 +312,8 @@ main() { test("doesn't buffer events from a broadcast stream", () async { var controller = StreamController.broadcast(); streamGroup.add(controller.stream); - controller.add("first"); - controller.addError("second"); + controller.add('first'); + controller.addError('second'); controller.close(); await flushMicrotasks(); @@ -327,16 +327,16 @@ main() { streamGroup.add(controller.stream); expect( - streamGroup.stream.toList(), completion(equals(["first", "second"]))); + streamGroup.stream.toList(), completion(equals(['first', 'second']))); - controller.add("first"); - controller.add("second"); + controller.add('first'); + controller.add('second'); controller.close(); expect(streamGroup.close(), completes); }); - test("cancels and re-listens broadcast streams", () async { + test('cancels and re-listens broadcast streams', () async { var subscription = streamGroup.stream.listen(null); var controller = StreamController.broadcast(); @@ -354,7 +354,7 @@ main() { expect(controller.hasListener, isTrue); }); - test("never cancels single-subscription streams", () async { + test('never cancels single-subscription streams', () async { var subscription = streamGroup.stream.listen(null); var controller = @@ -370,7 +370,7 @@ main() { await flushMicrotasks(); }); - test("drops events from a single-subscription stream while dormant", + test('drops events from a single-subscription stream while dormant', () async { var events = []; var subscription = streamGroup.stream.listen(events.add); @@ -379,22 +379,22 @@ main() { streamGroup.add(controller.stream); await flushMicrotasks(); - controller.add("first"); + controller.add('first'); await flushMicrotasks(); - expect(events, equals(["first"])); + expect(events, equals(['first'])); subscription.cancel(); - controller.add("second"); + controller.add('second'); await flushMicrotasks(); - expect(events, equals(["first"])); + expect(events, equals(['first'])); streamGroup.stream.listen(events.add); - controller.add("third"); + controller.add('third'); await flushMicrotasks(); - expect(events, equals(["first", "third"])); + expect(events, equals(['first', 'third'])); }); - test("a single-subscription stream can be removed while dormant", () async { + test('a single-subscription stream can be removed while dormant', () async { var controller = StreamController(); streamGroup.add(controller.stream); await flushMicrotasks(); @@ -407,61 +407,61 @@ main() { await flushMicrotasks(); expect(streamGroup.stream.toList(), completion(isEmpty)); - controller.add("first"); + controller.add('first'); expect(streamGroup.close(), completes); }); }); - group("regardless of type", () { - group("single-subscription", () { + group('regardless of type', () { + group('single-subscription', () { regardlessOfType(() => StreamGroup()); }); - group("broadcast", () { + group('broadcast', () { regardlessOfType(() => StreamGroup.broadcast()); }); }); - test("merge() emits events from all components streams", () async { + test('merge() emits events from all components streams', () async { var controller1 = StreamController(); var controller2 = StreamController(); var merged = StreamGroup.merge([controller1.stream, controller2.stream]); - controller1.add("first"); + controller1.add('first'); controller1.close(); - controller2.add("second"); + controller2.add('second'); controller2.close(); - expect(await merged.toList(), ["first", "second"]); + expect(await merged.toList(), ['first', 'second']); }); - test("mergeBroadcast() emits events from all components streams", () async { + test('mergeBroadcast() emits events from all components streams', () async { var controller1 = StreamController(); var controller2 = StreamController(); var merged = StreamGroup.mergeBroadcast([controller1.stream, controller2.stream]); - controller1.add("first"); + controller1.add('first'); controller1.close(); - controller2.add("second"); + controller2.add('second'); controller2.close(); expect(merged.isBroadcast, isTrue); - expect(await merged.toList(), ["first", "second"]); + expect(await merged.toList(), ['first', 'second']); }); } -void regardlessOfType(StreamGroup newStreamGroup()) { +void regardlessOfType(StreamGroup Function() newStreamGroup) { StreamGroup streamGroup; setUp(() { streamGroup = newStreamGroup(); }); - group("add()", () { - group("while dormant", () { + group('add()', () { + group('while dormant', () { test("doesn't listen to the stream until the group is listened to", () async { var controller = StreamController(); @@ -475,7 +475,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(controller.hasListener, isTrue); }); - test("is a no-op if the stream is already in the group", () { + test('is a no-op if the stream is already in the group', () { var controller = StreamController(); streamGroup.add(controller.stream); streamGroup.add(controller.stream); @@ -487,13 +487,13 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); }); - group("while active", () { + group('while active', () { setUp(() async { streamGroup.stream.listen(null); await flushMicrotasks(); }); - test("listens to the stream immediately", () async { + test('listens to the stream immediately', () async { var controller = StreamController(); expect(streamGroup.add(controller.stream), isNull); @@ -501,7 +501,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(controller.hasListener, isTrue); }); - test("is a no-op if the stream is already in the group", () async { + test('is a no-op if the stream is already in the group', () async { var controller = StreamController(); // If the stream were actually listened to more than once, future @@ -513,28 +513,28 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); }); - group("remove()", () { - group("while dormant", () { + group('remove()', () { + group('while dormant', () { test("stops emitting events for a stream that's removed", () async { var controller = StreamController(); streamGroup.add(controller.stream); - expect(streamGroup.stream.toList(), completion(equals(["first"]))); + expect(streamGroup.stream.toList(), completion(equals(['first']))); - controller.add("first"); + controller.add('first'); await flushMicrotasks(); - controller.add("second"); + controller.add('second'); expect(streamGroup.remove(controller.stream), completion(null)); expect(streamGroup.close(), completes); }); - test("is a no-op for an unknown stream", () { + test('is a no-op for an unknown stream', () { var controller = StreamController(); expect(streamGroup.remove(controller.stream), isNull); }); - test("and closed closes the group when the last stream is removed", + test('and closed closes the group when the last stream is removed', () async { var controller1 = StreamController(); var controller2 = StreamController(); @@ -555,7 +555,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); }); - group("while listening", () { + group('while listening', () { test("doesn't emit events from a removed stream", () { var controller = StreamController(); streamGroup.add(controller.stream); @@ -565,9 +565,9 @@ void regardlessOfType(StreamGroup newStreamGroup()) { // removal. This is documented in [StreamGroup.remove]. expect(streamGroup.stream.toList(), completion(isEmpty)); - controller.add("first"); + controller.add('first'); expect(streamGroup.remove(controller.stream), completion(null)); - controller.add("second"); + controller.add('second'); expect(streamGroup.close(), completes); }); @@ -585,18 +585,18 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(controller.hasListener, isFalse); }); - test("forwards cancel errors", () async { + test('forwards cancel errors', () async { var controller = - StreamController(onCancel: () => throw "error"); + StreamController(onCancel: () => throw 'error'); streamGroup.add(controller.stream); streamGroup.stream.listen(null); await flushMicrotasks(); - expect(streamGroup.remove(controller.stream), throwsA("error")); + expect(streamGroup.remove(controller.stream), throwsA('error')); }); - test("forwards cancel futures", () async { + test('forwards cancel futures', () async { var completer = Completer(); var controller = StreamController(onCancel: () => completer.future); @@ -618,7 +618,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(fired, isTrue); }); - test("is a no-op for an unknown stream", () async { + test('is a no-op for an unknown stream', () async { var controller = StreamController(); streamGroup.stream.listen(null); await flushMicrotasks(); @@ -626,7 +626,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { expect(streamGroup.remove(controller.stream), isNull); }); - test("and closed closes the group when the last stream is removed", + test('and closed closes the group when the last stream is removed', () async { var done = false; streamGroup.stream.listen(null, onDone: () => done = true); @@ -652,15 +652,15 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); }); - group("close()", () { - group("while dormant", () { - test("if there are no streams, closes the group", () { + group('close()', () { + group('while dormant', () { + test('if there are no streams, closes the group', () { expect(streamGroup.close(), completes); expect(streamGroup.stream.toList(), completion(isEmpty)); }); test( - "if there are streams, closes the group once those streams close " + 'if there are streams, closes the group once those streams close ' "and there's a listener", () async { var controller1 = StreamController(); var controller2 = StreamController(); @@ -677,13 +677,13 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); }); - group("while active", () { - test("if there are no streams, closes the group", () { + group('while active', () { + test('if there are no streams, closes the group', () { expect(streamGroup.stream.toList(), completion(isEmpty)); expect(streamGroup.close(), completes); }); - test("if there are streams, closes the group once those streams close", + test('if there are streams, closes the group once those streams close', () async { var done = false; streamGroup.stream.listen(null, onDone: () => done = true); @@ -710,7 +710,7 @@ void regardlessOfType(StreamGroup newStreamGroup()) { }); }); - test("returns a Future that completes once all events are dispatched", + test('returns a Future that completes once all events are dispatched', () async { var events = []; streamGroup.stream.listen(events.add); @@ -722,16 +722,16 @@ void regardlessOfType(StreamGroup newStreamGroup()) { // Add a bunch of events. Each of these will get dispatched in a // separate microtask, so we can test that [close] only completes once // all of them have dispatched. - controller.add("one"); - controller.add("two"); - controller.add("three"); - controller.add("four"); - controller.add("five"); - controller.add("six"); + controller.add('one'); + controller.add('two'); + controller.add('three'); + controller.add('four'); + controller.add('five'); + controller.add('six'); controller.close(); await streamGroup.close(); - expect(events, equals(["one", "two", "three", "four", "five", "six"])); + expect(events, equals(['one', 'two', 'three', 'four', 'five', 'six'])); }); }); } diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index b2a8625d..fa286a8e 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -2,16 +2,16 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE filevents. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; -main() { - group("source stream", () { - test("is listened to on first request, paused between requests", () async { +void main() { + group('source stream', () { + test('is listened to on first request, paused between requests', () async { var controller = StreamController(); var events = StreamQueue(controller.stream); await flushMicrotasks(); @@ -42,8 +42,8 @@ main() { }); }); - group("eventsDispatched", () { - test("increments after a next future completes", () async { + group('eventsDispatched', () { + test('increments after a next future completes', () async { var events = StreamQueue(createStream()); expect(events.eventsDispatched, equals(0)); @@ -60,13 +60,13 @@ main() { expect(events.eventsDispatched, equals(2)); }); - test("increments multiple times for multi-value requests", () async { + test('increments multiple times for multi-value requests', () async { var events = StreamQueue(createStream()); await events.take(3); expect(events.eventsDispatched, equals(3)); }); - test("increments multiple times for an accepted transaction", () async { + test('increments multiple times for an accepted transaction', () async { var events = StreamQueue(createStream()); await events.withTransaction((queue) async { await queue.next; @@ -83,8 +83,8 @@ main() { }); }); - group("lookAhead operation", () { - test("as simple list of events", () async { + group('lookAhead operation', () { + test('as simple list of events', () async { var events = StreamQueue(createStream()); expect(await events.lookAhead(4), [1, 2, 3, 4]); expect(await events.next, 1); @@ -94,7 +94,7 @@ main() { await events.cancel(); }); - test("of 0 events", () async { + test('of 0 events', () async { var events = StreamQueue(createStream()); expect(events.lookAhead(0), completion([])); expect(events.next, completion(1)); @@ -110,7 +110,7 @@ main() { await events.cancel(); }); - test("with bad arguments throws", () async { + test('with bad arguments throws', () async { var events = StreamQueue(createStream()); expect(() => events.lookAhead(-1), throwsArgumentError); expect(await events.next, 1); // Did not consume event. @@ -119,13 +119,13 @@ main() { await events.cancel(); }); - test("of too many arguments", () async { + test('of too many arguments', () async { var events = StreamQueue(createStream()); expect(await events.lookAhead(6), [1, 2, 3, 4]); await events.cancel(); }); - test("too large later", () async { + test('too large later', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -133,25 +133,25 @@ main() { await events.cancel(); }); - test("error", () async { + test('error', () async { var events = StreamQueue(createErrorStream()); - expect(events.lookAhead(4), throwsA("To err is divine!")); - expect(events.take(4), throwsA("To err is divine!")); + expect(events.lookAhead(4), throwsA('To err is divine!')); + expect(events.take(4), throwsA('To err is divine!')); expect(await events.next, 4); await events.cancel(); }); }); - group("next operation", () { - test("simple sequence of requests", () async { + group('next operation', () { + test('simple sequence of requests', () async { var events = StreamQueue(createStream()); - for (int i = 1; i <= 4; i++) { + for (var i = 1; i <= 4; i++) { expect(await events.next, i); } expect(events.next, throwsStateError); }); - test("multiple requests at the same time", () async { + test('multiple requests at the same time', () async { var events = StreamQueue(createStream()); var result = await Future.wait( [events.next, events.next, events.next, events.next]); @@ -159,18 +159,18 @@ main() { await events.cancel(); }); - test("sequence of requests with error", () async { + test('sequence of requests with error', () async { var events = StreamQueue(createErrorStream()); expect(await events.next, 1); expect(await events.next, 2); - expect(events.next, throwsA("To err is divine!")); + expect(events.next, throwsA('To err is divine!')); expect(await events.next, 4); await events.cancel(); }); }); - group("skip operation", () { - test("of two elements in the middle of sequence", () async { + group('skip operation', () { + test('of two elements in the middle of sequence', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.skip(2), 0); @@ -178,7 +178,7 @@ main() { await events.cancel(); }); - test("with negative/bad arguments throws", () async { + test('with negative/bad arguments throws', () async { var events = StreamQueue(createStream()); expect(() => events.skip(-1), throwsArgumentError); // A non-int throws either a type error or an argument error, @@ -189,7 +189,7 @@ main() { await events.cancel(); }); - test("of 0 elements works", () async { + test('of 0 elements works', () async { var events = StreamQueue(createStream()); expect(events.skip(0), completion(0)); expect(events.next, completion(1)); @@ -205,13 +205,13 @@ main() { await events.cancel(); }); - test("of too many events ends at stream start", () async { + test('of too many events ends at stream start', () async { var events = StreamQueue(createStream()); expect(await events.skip(6), 2); await events.cancel(); }); - test("of too many events after some events", () async { + test('of too many events after some events', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -219,7 +219,7 @@ main() { await events.cancel(); }); - test("of too many events ends at stream end", () async { + test('of too many events ends at stream end', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -229,20 +229,20 @@ main() { await events.cancel(); }); - test("of events with error", () async { + test('of events with error', () async { var events = StreamQueue(createErrorStream()); - expect(events.skip(4), throwsA("To err is divine!")); + expect(events.skip(4), throwsA('To err is divine!')); expect(await events.next, 4); await events.cancel(); }); - test("of events with error, and skip again after", () async { + test('of events with error, and skip again after', () async { var events = StreamQueue(createErrorStream()); - expect(events.skip(4), throwsA("To err is divine!")); + expect(events.skip(4), throwsA('To err is divine!')); expect(events.skip(2), completion(1)); await events.cancel(); }); - test("multiple skips at same time complete in order.", () async { + test('multiple skips at same time complete in order.', () async { var events = StreamQueue(createStream()); var skip1 = events.skip(1); var skip2 = events.skip(0); @@ -266,8 +266,8 @@ main() { }); }); - group("take operation", () { - test("as simple take of events", () async { + group('take operation', () { + test('as simple take of events', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.take(2), [2, 3]); @@ -275,7 +275,7 @@ main() { await events.cancel(); }); - test("of 0 events", () async { + test('of 0 events', () async { var events = StreamQueue(createStream()); expect(events.take(0), completion([])); expect(events.next, completion(1)); @@ -291,7 +291,7 @@ main() { await events.cancel(); }); - test("with bad arguments throws", () async { + test('with bad arguments throws', () async { var events = StreamQueue(createStream()); expect(() => events.take(-1), throwsArgumentError); expect(await events.next, 1); // Did not consume event. @@ -300,13 +300,13 @@ main() { await events.cancel(); }); - test("of too many arguments", () async { + test('of too many arguments', () async { var events = StreamQueue(createStream()); expect(await events.take(6), [1, 2, 3, 4]); await events.cancel(); }); - test("too large later", () async { + test('too large later', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -314,27 +314,27 @@ main() { await events.cancel(); }); - test("error", () async { + test('error', () async { var events = StreamQueue(createErrorStream()); - expect(events.take(4), throwsA("To err is divine!")); + expect(events.take(4), throwsA('To err is divine!')); expect(await events.next, 4); await events.cancel(); }); }); - group("rest operation", () { - test("after single next", () async { + group('rest operation', () { + test('after single next', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.rest.toList(), [2, 3, 4]); }); - test("at start", () async { + test('at start', () async { var events = StreamQueue(createStream()); expect(await events.rest.toList(), [1, 2, 3, 4]); }); - test("at end", () async { + test('at end', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -343,7 +343,7 @@ main() { expect(await events.rest.toList(), isEmpty); }); - test("after end", () async { + test('after end', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -353,13 +353,13 @@ main() { expect(await events.rest.toList(), isEmpty); }); - test("after receiving done requested before", () async { + test('after receiving done requested before', () async { var events = StreamQueue(createStream()); var next1 = events.next; var next2 = events.next; var next3 = events.next; var rest = events.rest; - for (int i = 0; i < 10; i++) { + for (var i = 0; i < 10; i++) { await flushMicrotasks(); } expect(await next1, 1); @@ -368,17 +368,17 @@ main() { expect(await rest.toList(), [4]); }); - test("with an error event error", () async { + test('with an error event error', () async { var events = StreamQueue(createErrorStream()); expect(await events.next, 1); var rest = events.rest; var events2 = StreamQueue(rest); expect(await events2.next, 2); - expect(events2.next, throwsA("To err is divine!")); + expect(events2.next, throwsA('To err is divine!')); expect(await events2.next, 4); }); - test("closes the events, prevents other operations", () async { + test('closes the events, prevents other operations', () async { var events = StreamQueue(createStream()); var stream = events.rest; expect(() => events.next, throwsStateError); @@ -389,7 +389,7 @@ main() { expect(stream.toList(), completion([1, 2, 3, 4])); }); - test("forwards to underlying stream", () async { + test('forwards to underlying stream', () async { var cancel = Completer(); var controller = StreamController(onCancel: () => cancel.future); var events = StreamQueue(controller.stream); @@ -436,8 +436,8 @@ main() { }); }); - group("peek operation", () { - test("peeks one event", () async { + group('peek operation', () { + test('peeks one event', () async { var events = StreamQueue(createStream()); expect(await events.peek, 1); expect(await events.next, 1); @@ -449,28 +449,28 @@ main() { expect(events.peek, throwsA(anything)); await events.cancel(); }); - test("multiple requests at the same time", () async { + test('multiple requests at the same time', () async { var events = StreamQueue(createStream()); var result = await Future.wait( [events.peek, events.peek, events.next, events.peek, events.peek]); expect(result, [1, 1, 1, 2, 2]); await events.cancel(); }); - test("sequence of requests with error", () async { + test('sequence of requests with error', () async { var events = StreamQueue(createErrorStream()); expect(await events.next, 1); expect(await events.next, 2); - expect(events.peek, throwsA("To err is divine!")); + expect(events.peek, throwsA('To err is divine!')); // Error stays in queue. - expect(events.peek, throwsA("To err is divine!")); - expect(events.next, throwsA("To err is divine!")); + expect(events.peek, throwsA('To err is divine!')); + expect(events.next, throwsA('To err is divine!')); expect(await events.next, 4); await events.cancel(); }); }); - group("cancel operation", () { - test("closes the events, prevents any other operation", () async { + group('cancel operation', () { + test('closes the events, prevents any other operation', () async { var events = StreamQueue(createStream()); await events.cancel(); expect(() => events.lookAhead(1), throwsStateError); @@ -482,7 +482,7 @@ main() { expect(() => events.cancel(), throwsStateError); }); - test("cancels underlying subscription when called before any event", + test('cancels underlying subscription when called before any event', () async { var cancelFuture = Future.value(42); var controller = StreamController(onCancel: () => cancelFuture); @@ -490,7 +490,7 @@ main() { expect(await events.cancel(), 42); }); - test("cancels underlying subscription, returns result", () async { + test('cancels underlying subscription, returns result', () async { var cancelFuture = Future.value(42); var controller = StreamController(onCancel: () => cancelFuture); var events = StreamQueue(controller.stream); @@ -499,8 +499,8 @@ main() { expect(await events.cancel(), 42); }); - group("with immediate: true", () { - test("closes the events, prevents any other operation", () async { + group('with immediate: true', () { + test('closes the events, prevents any other operation', () async { var events = StreamQueue(createStream()); await events.cancel(immediate: true); expect(() => events.next, throwsStateError); @@ -510,7 +510,7 @@ main() { expect(() => events.cancel(), throwsStateError); }); - test("cancels the underlying subscription immediately", () async { + test('cancels the underlying subscription immediately', () async { var controller = StreamController(); controller.add(1); @@ -522,7 +522,7 @@ main() { expect(controller.hasListener, isFalse); }); - test("cancels the underlying subscription when called before any event", + test('cancels the underlying subscription when called before any event', () async { var cancelFuture = Future.value(42); var controller = StreamController(onCancel: () => cancelFuture); @@ -531,7 +531,7 @@ main() { expect(await events.cancel(immediate: true), 42); }); - test("closes pending requests", () async { + test('closes pending requests', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(events.next, throwsStateError); @@ -540,7 +540,7 @@ main() { await events.cancel(immediate: true); }); - test("returns the result of closing the underlying subscription", + test('returns the result of closing the underlying subscription', () async { var controller = StreamController(onCancel: () => Future.value(42)); @@ -564,30 +564,30 @@ main() { }); }); - group("hasNext operation", () { - test("true at start", () async { + group('hasNext operation', () { + test('true at start', () async { var events = StreamQueue(createStream()); expect(await events.hasNext, isTrue); }); - test("true after start", () async { + test('true after start', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, isTrue); }); - test("true at end", () async { + test('true at end', () async { var events = StreamQueue(createStream()); - for (int i = 1; i <= 4; i++) { + for (var i = 1; i <= 4; i++) { expect(await events.next, i); } expect(await events.hasNext, isFalse); }); - test("true when enqueued", () async { + test('true when enqueued', () async { var events = StreamQueue(createStream()); var values = []; - for (int i = 1; i <= 3; i++) { + for (var i = 1; i <= 3; i++) { events.next.then(values.add); } expect(values, isEmpty); @@ -595,10 +595,10 @@ main() { expect(values, [1, 2, 3]); }); - test("false when enqueued", () async { + test('false when enqueued', () async { var events = StreamQueue(createStream()); var values = []; - for (int i = 1; i <= 4; i++) { + for (var i = 1; i <= 4; i++) { events.next.then(values.add); } expect(values, isEmpty); @@ -606,7 +606,7 @@ main() { expect(values, [1, 2, 3, 4]); }); - test("true when data event", () async { + test('true when data event', () async { var controller = StreamController(); var events = StreamQueue(controller.stream); @@ -622,7 +622,7 @@ main() { expect(hasNext, isTrue); }); - test("true when error event", () async { + test('true when error event', () async { var controller = StreamController(); var events = StreamQueue(controller.stream); @@ -632,14 +632,14 @@ main() { }); await flushMicrotasks(); expect(hasNext, isNull); - controller.addError("BAD"); + controller.addError('BAD'); expect(hasNext, isNull); await flushMicrotasks(); expect(hasNext, isTrue); - expect(events.next, throwsA("BAD")); + expect(events.next, throwsA('BAD')); }); - test("- hasNext after hasNext", () async { + test('- hasNext after hasNext', () async { var events = StreamQueue(createStream()); expect(await events.hasNext, true); expect(await events.hasNext, true); @@ -657,7 +657,7 @@ main() { expect(await events.hasNext, false); }); - test("- next after true", () async { + test('- next after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -665,7 +665,7 @@ main() { expect(await events.next, 3); }); - test("- next after true, enqueued", () async { + test('- next after true, enqueued', () async { var events = StreamQueue(createStream()); var responses = []; events.next.then(responses.add); @@ -677,7 +677,7 @@ main() { expect(responses, [1, true, 2]); }); - test("- skip 0 after true", () async { + test('- skip 0 after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -685,7 +685,7 @@ main() { expect(await events.next, 2); }); - test("- skip 1 after true", () async { + test('- skip 1 after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -693,7 +693,7 @@ main() { expect(await events.next, 3); }); - test("- skip 2 after true", () async { + test('- skip 2 after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -701,7 +701,7 @@ main() { expect(await events.next, 4); }); - test("- take 0 after true", () async { + test('- take 0 after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -709,7 +709,7 @@ main() { expect(await events.next, 2); }); - test("- take 1 after true", () async { + test('- take 1 after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -717,7 +717,7 @@ main() { expect(await events.next, 3); }); - test("- take 2 after true", () async { + test('- take 2 after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -725,7 +725,7 @@ main() { expect(await events.next, 4); }); - test("- rest after true", () async { + test('- rest after true', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.hasNext, true); @@ -733,7 +733,7 @@ main() { expect(await stream.toList(), [2, 3, 4]); }); - test("- rest after true, at last", () async { + test('- rest after true, at last', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -743,7 +743,7 @@ main() { expect(await stream.toList(), [4]); }); - test("- rest after false", () async { + test('- rest after false', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -754,7 +754,7 @@ main() { expect(await stream.toList(), isEmpty); }); - test("- cancel after true on data", () async { + test('- cancel after true on data', () async { var events = StreamQueue(createStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -762,7 +762,7 @@ main() { expect(await events.cancel(), null); }); - test("- cancel after true on error", () async { + test('- cancel after true on error', () async { var events = StreamQueue(createErrorStream()); expect(await events.next, 1); expect(await events.next, 2); @@ -771,7 +771,7 @@ main() { }); }); - group("startTransaction operation produces a transaction that", () { + group('startTransaction operation produces a transaction that', () { StreamQueue events; StreamQueueTransaction transaction; StreamQueue queue1; @@ -784,8 +784,8 @@ main() { queue2 = transaction.newQueue(); }); - group("emits queues that", () { - test("independently emit events", () async { + group('emits queues that', () { + test('independently emit events', () async { expect(await queue1.next, 2); expect(await queue2.next, 2); expect(await queue2.next, 3); @@ -796,7 +796,7 @@ main() { expect(await queue2.hasNext, isFalse); }); - test("queue requests for events", () async { + test('queue requests for events', () async { expect(queue1.next, completion(2)); expect(queue2.next, completion(2)); expect(queue2.next, completion(3)); @@ -807,7 +807,7 @@ main() { expect(queue2.hasNext, completion(isFalse)); }); - test("independently emit errors", () async { + test('independently emit errors', () async { events = StreamQueue(createErrorStream()); expect(await events.next, 1); transaction = events.startTransaction(); @@ -816,8 +816,8 @@ main() { expect(queue1.next, completion(2)); expect(queue2.next, completion(2)); - expect(queue2.next, throwsA("To err is divine!")); - expect(queue1.next, throwsA("To err is divine!")); + expect(queue2.next, throwsA('To err is divine!')); + expect(queue1.next, throwsA('To err is divine!')); expect(queue1.next, completion(4)); expect(queue2.next, completion(4)); expect(queue1.hasNext, completion(isFalse)); @@ -825,8 +825,8 @@ main() { }); }); - group("when rejected", () { - test("further original requests use the previous state", () async { + group('when rejected', () { + test('further original requests use the previous state', () async { expect(await queue1.next, 2); expect(await queue2.next, 2); expect(await queue2.next, 3); @@ -840,7 +840,7 @@ main() { expect(await events.hasNext, isFalse); }); - test("pending original requests use the previous state", () async { + test('pending original requests use the previous state', () async { expect(await queue1.next, 2); expect(await queue2.next, 2); expect(await queue2.next, 3); @@ -853,7 +853,7 @@ main() { transaction.reject(); }); - test("further child requests act as though the stream was closed", + test('further child requests act as though the stream was closed', () async { expect(await queue1.next, 2); transaction.reject(); @@ -862,7 +862,7 @@ main() { expect(queue1.next, throwsStateError); }); - test("pending child requests act as though the stream was closed", + test('pending child requests act as though the stream was closed', () async { expect(await queue1.next, 2); expect(queue1.hasNext, completion(isFalse)); @@ -871,7 +871,7 @@ main() { }); // Regression test. - test("pending child rest requests emit no more events", () async { + test('pending child rest requests emit no more events', () async { var controller = StreamController(); var events = StreamQueue(controller.stream); var transaction = events.startTransaction(); @@ -899,13 +899,13 @@ main() { await queue1.cancel(); }); - test("calls to commit() or reject() fail", () async { + test('calls to commit() or reject() fail', () async { transaction.reject(); expect(transaction.reject, throwsStateError); expect(() => transaction.commit(queue1), throwsStateError); }); - test("before the transaction emits any events, does nothing", () async { + test('before the transaction emits any events, does nothing', () async { var controller = StreamController(); var events = StreamQueue(controller.stream); @@ -924,22 +924,22 @@ main() { }); }); - group("when committed", () { - test("further original requests use the committed state", () async { + group('when committed', () { + test('further original requests use the committed state', () async { expect(await queue1.next, 2); await flushMicrotasks(); transaction.commit(queue1); expect(await events.next, 3); }); - test("pending original requests use the committed state", () async { + test('pending original requests use the committed state', () async { expect(await queue1.next, 2); expect(events.next, completion(3)); await flushMicrotasks(); transaction.commit(queue1); }); - test("further child requests act as though the stream was closed", + test('further child requests act as though the stream was closed', () async { expect(await queue2.next, 2); transaction.commit(queue2); @@ -948,7 +948,7 @@ main() { expect(queue1.next, throwsStateError); }); - test("pending child requests act as though the stream was closed", + test('pending child requests act as though the stream was closed', () async { expect(await queue2.next, 2); expect(queue1.hasNext, completion(isFalse)); @@ -956,7 +956,7 @@ main() { transaction.commit(queue2); }); - test("further requests act as though the stream was closed", () async { + test('further requests act as though the stream was closed', () async { expect(await queue1.next, 2); transaction.commit(queue1); @@ -964,25 +964,25 @@ main() { expect(queue1.next, throwsStateError); }); - test("cancel() may still be called explicitly", () async { + test('cancel() may still be called explicitly', () async { expect(await queue1.next, 2); transaction.commit(queue1); await queue1.cancel(); }); - test("throws if there are pending requests", () async { + test('throws if there are pending requests', () async { expect(await queue1.next, 2); expect(queue1.hasNext, completion(isTrue)); expect(() => transaction.commit(queue1), throwsStateError); }); - test("calls to commit() or reject() fail", () async { + test('calls to commit() or reject() fail', () async { transaction.commit(queue1); expect(transaction.reject, throwsStateError); expect(() => transaction.commit(queue1), throwsStateError); }); - test("before the transaction emits any events, does nothing", () async { + test('before the transaction emits any events, does nothing', () async { var controller = StreamController(); var events = StreamQueue(controller.stream); @@ -1003,14 +1003,14 @@ main() { }); }); - group("withTransaction operation", () { + group('withTransaction operation', () { StreamQueue events; setUp(() async { events = StreamQueue(createStream()); expect(await events.next, 1); }); - test("passes a copy of the parent queue", () async { + test('passes a copy of the parent queue', () async { await events.withTransaction(expectAsync1((queue) async { expect(await queue.next, 2); expect(await queue.next, 3); @@ -1021,8 +1021,8 @@ main() { }); test( - "the parent queue continues from the child position if it returns " - "true", () async { + 'the parent queue continues from the child position if it returns ' + 'true', () async { await events.withTransaction(expectAsync1((queue) async { expect(await queue.next, 2); return true; @@ -1032,8 +1032,8 @@ main() { }); test( - "the parent queue continues from its original position if it returns " - "false", () async { + 'the parent queue continues from its original position if it returns ' + 'false', () async { await events.withTransaction(expectAsync1((queue) async { expect(await queue.next, 2); return false; @@ -1042,29 +1042,29 @@ main() { expect(await events.next, 2); }); - test("the parent queue continues from the child position if it throws", () { + test('the parent queue continues from the child position if it throws', () { expect(events.withTransaction(expectAsync1((queue) async { expect(await queue.next, 2); - throw "oh no"; - })), throwsA("oh no")); + throw 'oh no'; + })), throwsA('oh no')); expect(events.next, completion(3)); }); - test("returns whether the transaction succeeded", () { + test('returns whether the transaction succeeded', () { expect(events.withTransaction((_) async => true), completion(isTrue)); expect(events.withTransaction((_) async => false), completion(isFalse)); }); }); - group("cancelable operation", () { + group('cancelable operation', () { StreamQueue events; setUp(() async { events = StreamQueue(createStream()); expect(await events.next, 1); }); - test("passes a copy of the parent queue", () async { + test('passes a copy of the parent queue', () async { await events.cancelable(expectAsync1((queue) async { expect(await queue.next, 2); expect(await queue.next, 3); @@ -1073,7 +1073,7 @@ main() { })).value; }); - test("the parent queue continues from the child position by default", + test('the parent queue continues from the child position by default', () async { await events.cancelable(expectAsync1((queue) async { expect(await queue.next, 2); @@ -1083,19 +1083,19 @@ main() { }); test( - "the parent queue continues from the child position if an error is " - "thrown", () async { + 'the parent queue continues from the child position if an error is ' + 'thrown', () async { expect( events.cancelable(expectAsync1((queue) async { expect(await queue.next, 2); - throw "oh no"; + throw 'oh no'; })).value, - throwsA("oh no")); + throwsA('oh no')); expect(events.next, completion(3)); }); - test("the parent queue continues from the original position if canceled", + test('the parent queue continues from the original position if canceled', () async { var operation = events.cancelable(expectAsync1((queue) async { expect(await queue.next, 2); @@ -1105,17 +1105,17 @@ main() { expect(await events.next, 2); }); - test("forwards the value from the callback", () async { + test('forwards the value from the callback', () async { expect( await events.cancelable(expectAsync1((queue) async { expect(await queue.next, 2); - return "value"; + return 'value'; })).value, - "value"); + 'value'); }); }); - test("all combinations sequential skip/next/take operations", () async { + test('all combinations sequential skip/next/take operations', () async { // Takes all combinations of two of next, skip and take, then ends with // doing rest. Each of the first rounds do 10 events of each type, // the rest does 20 elements. @@ -1124,30 +1124,30 @@ main() { // Test expecting [startIndex .. startIndex + 9] as events using // `next`. - nextTest(startIndex) { - for (int i = 0; i < 10; i++) { + void nextTest(startIndex) { + for (var i = 0; i < 10; i++) { expect(events.next, completion(startIndex + i)); } } // Test expecting 10 events to be skipped. - skipTest(startIndex) { + void skipTest(startIndex) { expect(events.skip(10), completion(0)); } // Test expecting [startIndex .. startIndex + 9] as events using // `take(10)`. - takeTest(startIndex) { + void takeTest(startIndex) { expect(events.take(10), completion(List.generate(10, (i) => startIndex + i))); } var tests = [nextTest, skipTest, takeTest]; - int counter = 0; + var counter = 0; // Run through all pairs of two tests and run them. - for (int i = 0; i < tests.length; i++) { - for (int j = 0; j < tests.length; j++) { + for (var i = 0; i < tests.length; i++) { + for (var j = 0; j < tests.length; j++) { tests[i](counter); tests[j](counter + 10); counter += 20; @@ -1178,7 +1178,7 @@ Stream createErrorStream() { await flushMicrotasks(); controller.add(2); await flushMicrotasks(); - controller.addError("To err is divine!"); + controller.addError('To err is divine!'); await flushMicrotasks(); controller.add(4); await flushMicrotasks(); @@ -1188,5 +1188,7 @@ Stream createErrorStream() { } Stream createLongStream(int eventCount) async* { - for (int i = 0; i < eventCount; i++) yield i; + for (var i = 0; i < eventCount; i++) { + yield i; + } } diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 56860ce9..69d1d8ad 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -2,21 +2,21 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; -main() { +void main() { StreamSinkCompleter completer; setUp(() { completer = StreamSinkCompleter(); }); - group("when a stream is linked before events are added", () { - test("data events are forwarded", () { + group('when a stream is linked before events are added', () { + test('data events are forwarded', () { var sink = TestSink(); completer.setDestinationSink(sink); completer.sink..add(1)..add(2)..add(3)..add(4); @@ -27,16 +27,16 @@ main() { expect(sink.results[3].asValue.value, equals(4)); }); - test("error events are forwarded", () { + test('error events are forwarded', () { var sink = TestSink(); completer.setDestinationSink(sink); - completer.sink..addError("oh no")..addError("that's bad"); + completer.sink..addError('oh no')..addError("that's bad"); - expect(sink.results[0].asError.error, equals("oh no")); + expect(sink.results[0].asError.error, equals('oh no')); expect(sink.results[1].asError.error, equals("that's bad")); }); - test("addStream is forwarded", () async { + test('addStream is forwarded', () async { var sink = TestSink(); completer.setDestinationSink(sink); @@ -44,13 +44,13 @@ main() { completer.sink.addStream(controller.stream); controller.add(1); - controller.addError("oh no"); + controller.addError('oh no'); controller.add(2); controller.addError("that's bad"); await flushMicrotasks(); expect(sink.results[0].asValue.value, equals(1)); - expect(sink.results[1].asError.error, equals("oh no")); + expect(sink.results[1].asError.error, equals('oh no')); expect(sink.results[2].asValue.value, equals(2)); expect(sink.results[3].asError.error, equals("that's bad")); expect(sink.isClosed, isFalse); @@ -60,14 +60,14 @@ main() { expect(sink.isClosed, isFalse); }); - test("close() is forwarded", () { + test('close() is forwarded', () { var sink = TestSink(); completer.setDestinationSink(sink); completer.sink.close(); expect(sink.isClosed, isTrue); }); - test("the future from the inner close() is returned", () async { + test('the future from the inner close() is returned', () async { var closeCompleter = Completer(); var sink = TestSink(onDone: () => closeCompleter.future); completer.setDestinationSink(sink); @@ -85,35 +85,35 @@ main() { expect(closeCompleted, isTrue); }); - test("errors are forwarded from the inner close()", () { - var sink = TestSink(onDone: () => throw "oh no"); + test('errors are forwarded from the inner close()', () { + var sink = TestSink(onDone: () => throw 'oh no'); completer.setDestinationSink(sink); - expect(completer.sink.done, throwsA("oh no")); - expect(completer.sink.close(), throwsA("oh no")); + expect(completer.sink.done, throwsA('oh no')); + expect(completer.sink.close(), throwsA('oh no')); }); test("errors aren't top-leveled if only close() is listened to", () async { - var sink = TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw 'oh no'); completer.setDestinationSink(sink); - expect(completer.sink.close(), throwsA("oh no")); + expect(completer.sink.close(), throwsA('oh no')); // Give the event loop a chance to top-level errors if it's going to. await flushMicrotasks(); }); test("errors aren't top-leveled if only done is listened to", () async { - var sink = TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw 'oh no'); completer.setDestinationSink(sink); completer.sink.close(); - expect(completer.sink.done, throwsA("oh no")); + expect(completer.sink.done, throwsA('oh no')); // Give the event loop a chance to top-level errors if it's going to. await flushMicrotasks(); }); }); - group("when a stream is linked after events are added", () { - test("data events are forwarded", () async { + group('when a stream is linked after events are added', () { + test('data events are forwarded', () async { completer.sink..add(1)..add(2)..add(3)..add(4); await flushMicrotasks(); @@ -127,24 +127,24 @@ main() { expect(sink.results[3].asValue.value, equals(4)); }); - test("error events are forwarded", () async { - completer.sink..addError("oh no")..addError("that's bad"); + test('error events are forwarded', () async { + completer.sink..addError('oh no')..addError("that's bad"); await flushMicrotasks(); var sink = TestSink(); completer.setDestinationSink(sink); await flushMicrotasks(); - expect(sink.results[0].asError.error, equals("oh no")); + expect(sink.results[0].asError.error, equals('oh no')); expect(sink.results[1].asError.error, equals("that's bad")); }); - test("addStream is forwarded", () async { + test('addStream is forwarded', () async { var controller = StreamController(); completer.sink.addStream(controller.stream); controller.add(1); - controller.addError("oh no"); + controller.addError('oh no'); controller.add(2); controller.addError("that's bad"); controller.close(); @@ -155,13 +155,13 @@ main() { await flushMicrotasks(); expect(sink.results[0].asValue.value, equals(1)); - expect(sink.results[1].asError.error, equals("oh no")); + expect(sink.results[1].asError.error, equals('oh no')); expect(sink.results[2].asValue.value, equals(2)); expect(sink.results[3].asError.error, equals("that's bad")); expect(sink.isClosed, isFalse); }); - test("close() is forwarded", () async { + test('close() is forwarded', () async { completer.sink.close(); await flushMicrotasks(); @@ -172,7 +172,7 @@ main() { expect(sink.isClosed, isTrue); }); - test("the future from the inner close() is returned", () async { + test('the future from the inner close() is returned', () async { var closeCompleted = false; completer.sink.close().then(expectAsync1((_) { closeCompleted = true; @@ -190,20 +190,20 @@ main() { expect(closeCompleted, isTrue); }); - test("errors are forwarded from the inner close()", () async { - expect(completer.sink.done, throwsA("oh no")); - expect(completer.sink.close(), throwsA("oh no")); + test('errors are forwarded from the inner close()', () async { + expect(completer.sink.done, throwsA('oh no')); + expect(completer.sink.close(), throwsA('oh no')); await flushMicrotasks(); - var sink = TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw 'oh no'); completer.setDestinationSink(sink); }); test("errors aren't top-leveled if only close() is listened to", () async { - expect(completer.sink.close(), throwsA("oh no")); + expect(completer.sink.close(), throwsA('oh no')); await flushMicrotasks(); - var sink = TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw 'oh no'); completer.setDestinationSink(sink); // Give the event loop a chance to top-level errors if it's going to. @@ -212,10 +212,10 @@ main() { test("errors aren't top-leveled if only done is listened to", () async { completer.sink.close(); - expect(completer.sink.done, throwsA("oh no")); + expect(completer.sink.done, throwsA('oh no')); await flushMicrotasks(); - var sink = TestSink(onDone: () => throw "oh no"); + var sink = TestSink(onDone: () => throw 'oh no'); completer.setDestinationSink(sink); // Give the event loop a chance to top-level errors if it's going to. @@ -223,7 +223,7 @@ main() { }); }); - test("the sink is closed, the destination is set, then done is read", + test('the sink is closed, the destination is set, then done is read', () async { expect(completer.sink.close(), completes); await flushMicrotasks(); @@ -234,7 +234,7 @@ main() { expect(completer.sink.done, completes); }); - test("done is read, the destination is set, then the sink is closed", + test('done is read, the destination is set, then the sink is closed', () async { expect(completer.sink.done, completes); await flushMicrotasks(); @@ -245,8 +245,8 @@ main() { expect(completer.sink.close(), completes); }); - group("fromFuture()", () { - test("with a successful completion", () async { + group('fromFuture()', () { + test('with a successful completion', () async { var futureCompleter = Completer(); var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); sink.add(1); @@ -263,27 +263,27 @@ main() { expect(testSink.results[2].asValue.value, equals(3)); }); - test("with an error", () async { + test('with an error', () async { var futureCompleter = Completer(); var sink = StreamSinkCompleter.fromFuture(futureCompleter.future); - expect(sink.done, throwsA("oh no")); - futureCompleter.completeError("oh no"); + expect(sink.done, throwsA('oh no')); + futureCompleter.completeError('oh no'); }); }); - group("setError()", () { - test("produces a closed sink with the error", () { - completer.setError("oh no"); - expect(completer.sink.done, throwsA("oh no")); - expect(completer.sink.close(), throwsA("oh no")); + group('setError()', () { + test('produces a closed sink with the error', () { + completer.setError('oh no'); + expect(completer.sink.done, throwsA('oh no')); + expect(completer.sink.close(), throwsA('oh no')); }); - test("produces an error even if done was accessed earlier", () async { - expect(completer.sink.done, throwsA("oh no")); - expect(completer.sink.close(), throwsA("oh no")); + test('produces an error even if done was accessed earlier', () async { + expect(completer.sink.done, throwsA('oh no')); + expect(completer.sink.close(), throwsA('oh no')); await flushMicrotasks(); - completer.setError("oh no"); + completer.setError('oh no'); }); }); diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index 5e981a64..8493971d 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -2,12 +2,12 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE filevents. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; void main() { StreamController controller; @@ -15,8 +15,8 @@ void main() { controller = StreamController(); }); - group("fromStreamTransformer", () { - test("transforms data events", () { + group('fromStreamTransformer', () { + test('transforms data events', () { var transformer = StreamSinkTransformer.fromStreamTransformer( StreamTransformer.fromHandlers(handleData: (i, sink) { sink.add(i * 2); @@ -34,7 +34,7 @@ void main() { sink.close(); }); - test("transforms error events", () { + test('transforms error events', () { var transformer = StreamSinkTransformer.fromStreamTransformer( StreamTransformer.fromHandlers(handleError: (i, stackTrace, sink) { sink.addError((i as num) * 2, stackTrace); @@ -55,7 +55,7 @@ void main() { sink.close(); }); - test("transforms done events", () { + test('transforms done events', () { var transformer = StreamSinkTransformer.fromStreamTransformer( StreamTransformer.fromHandlers(handleDone: (sink) { sink.add(1); @@ -71,7 +71,7 @@ void main() { sink.close(); }); - test("forwards the future from inner.close", () async { + test('forwards the future from inner.close', () async { var transformer = StreamSinkTransformer.fromStreamTransformer( StreamTransformer.fromHandlers()); var innerSink = CompleterStreamSink(); @@ -104,18 +104,18 @@ void main() { // This will close the inner sink, but it shouldn't top-level the error. sink.add(1); - innerSink.completer.completeError("oh no"); + innerSink.completer.completeError('oh no'); await flushMicrotasks(); // The error should be piped through done and close even if they're called // after the underlying sink is closed. - expect(sink.done, throwsA("oh no")); - expect(sink.close(), throwsA("oh no")); + expect(sink.done, throwsA('oh no')); + expect(sink.close(), throwsA('oh no')); }); }); - group("fromHandlers", () { - test("transforms data events", () { + group('fromHandlers', () { + test('transforms data events', () { var transformer = StreamSinkTransformer.fromHandlers(handleData: (i, sink) { sink.add(i * 2); @@ -133,7 +133,7 @@ void main() { sink.close(); }); - test("transforms error events", () { + test('transforms error events', () { var transformer = StreamSinkTransformer.fromHandlers( handleError: (i, stackTrace, sink) { sink.addError((i as num) * 2, stackTrace); @@ -154,7 +154,7 @@ void main() { sink.close(); }); - test("transforms done events", () { + test('transforms done events', () { var transformer = StreamSinkTransformer.fromHandlers(handleDone: (sink) { sink.add(1); sink.close(); @@ -169,7 +169,7 @@ void main() { sink.close(); }); - test("forwards the future from inner.close", () async { + test('forwards the future from inner.close', () async { var transformer = StreamSinkTransformer.fromHandlers(); var innerSink = CompleterStreamSink(); var sink = transformer.bind(innerSink); @@ -201,13 +201,13 @@ void main() { // This will close the inner sink, but it shouldn't top-level the error. sink.add(1); - innerSink.completer.completeError("oh no"); + innerSink.completer.completeError('oh no'); await flushMicrotasks(); // The error should be piped through done and close even if they're called // after the underlying sink is closed. - expect(sink.done, throwsA("oh no")); - expect(sink.close(), throwsA("oh no")); + expect(sink.done, throwsA('oh no')); + expect(sink.close(), throwsA('oh no')); }); }); } diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 497f1257..fd37f87c 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -7,7 +7,7 @@ import 'dart:async'; import 'package:async/async.dart'; import 'package:test/test.dart'; -main() { +void main() { StreamController controller; StreamSplitter splitter; setUp(() { @@ -37,12 +37,12 @@ main() { controller.close(); }); - test("a branch replays error events as well as data events", () { + test('a branch replays error events as well as data events', () { var branch = splitter.split(); splitter.close(); controller.add(1); - controller.addError("error"); + controller.addError('error'); controller.add(3); controller.close(); @@ -54,7 +54,7 @@ main() { count++; }, count: 2), onError: expectAsync1((error) { expect(count, equals(1)); - expect(error, equals("error")); + expect(error, equals('error')); count++; }), onDone: expectAsync0(() { expect(count, equals(3)); @@ -89,7 +89,7 @@ main() { splitter.close(); }); - test("creates single-subscription branches", () async { + test('creates single-subscription branches', () async { var branch = splitter.split(); expect(branch.isBroadcast, isFalse); branch.listen(null); @@ -101,7 +101,7 @@ main() { // 1.11 is released. In 1.10, the stream exposed by a StreamController didn't // have a reified type. - test("multiple branches each replay the stream", () async { + test('multiple branches each replay the stream', () async { var branch1 = splitter.split(); controller.add(1); controller.add(2); @@ -151,7 +151,7 @@ main() { expect(controller.hasListener, isTrue); }); - test("the source stream is paused when all branches are paused", () async { + test('the source stream is paused when all branches are paused', () async { var branch1 = splitter.split(); var branch2 = splitter.split(); var branch3 = splitter.split(); @@ -178,7 +178,7 @@ main() { expect(controller.isPaused, isFalse); }); - test("the source stream is paused when all branches are canceled", () async { + test('the source stream is paused when all branches are canceled', () async { var branch1 = splitter.split(); var branch2 = splitter.split(); var branch3 = splitter.split(); @@ -211,7 +211,7 @@ main() { test( "the source stream is canceled when it's closed after all branches have " - "been canceled", () async { + 'been canceled', () async { var branch1 = splitter.split(); var branch2 = splitter.split(); var branch3 = splitter.split(); @@ -237,8 +237,8 @@ main() { }); test( - "the source stream is canceled when all branches are canceled after it " - "has been closed", () async { + 'the source stream is canceled when all branches are canceled after it ' + 'has been closed', () async { var branch1 = splitter.split(); var branch2 = splitter.split(); var branch3 = splitter.split(); @@ -263,7 +263,7 @@ main() { test( "a splitter that's closed before any branches are added never listens " - "to the source stream", () { + 'to the source stream', () { splitter.close(); // This would throw an error if the stream had already been listened to. @@ -271,8 +271,8 @@ main() { }); test( - "splitFrom splits a source stream into the designated number of " - "branches", () { + 'splitFrom splits a source stream into the designated number of ' + 'branches', () { var branches = StreamSplitter.splitFrom(controller.stream, 5); controller.add(1); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 1e5f5249..f462996d 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -2,10 +2,10 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; /// Create an error with the same values as [base], except that it throwsA /// when seeing the value [errorValue]. @@ -16,10 +16,10 @@ Stream streamError(Stream base, int errorValue, error) { /// Make a [Stream] from an [Iterable] by adding events to a stream controller /// at periodic intervals. Stream mks(Iterable iterable) { - Iterator iterator = iterable.iterator; - StreamController controller = StreamController(); + var iterator = iterable.iterator; + var controller = StreamController(); // Some varying time between 3 and 10 ms. - int ms = ((++ctr) * 5) % 7 + 3; + var ms = ((++ctr) * 5) % 7 + 3; Timer.periodic(Duration(milliseconds: ms), (Timer timer) { if (iterator.moveNext()) { controller.add(iterator.current); @@ -34,17 +34,17 @@ Stream mks(Iterable iterable) { /// Counter used to give varying delays for streams. int ctr = 0; -main() { +void main() { // Test that zipping [streams] gives the results iterated by [expectedData]. - testZip(Iterable streams, Iterable expectedData) { - List data = []; + void testZip(Iterable streams, Iterable expectedData) { + var data = []; Stream zip = StreamZip(streams); zip.listen(data.add, onDone: expectAsync0(() { expect(data, equals(expectedData)); })); } - test("Basic", () { + test('Basic', () { testZip([ mks([1, 2, 3]), mks([4, 5, 6]), @@ -56,7 +56,7 @@ main() { ]); }); - test("Uneven length 1", () { + test('Uneven length 1', () { testZip([ mks([1, 2, 3, 99, 100]), mks([4, 5, 6]), @@ -68,7 +68,7 @@ main() { ]); }); - test("Uneven length 2", () { + test('Uneven length 2', () { testZip([ mks([1, 2, 3]), mks([4, 5, 6, 99, 100]), @@ -80,7 +80,7 @@ main() { ]); }); - test("Uneven length 3", () { + test('Uneven length 3', () { testZip([ mks([1, 2, 3]), mks([4, 5, 6]), @@ -92,7 +92,7 @@ main() { ]); }); - test("Uneven length 4", () { + test('Uneven length 4', () { testZip([ mks([1, 2, 3, 98]), mks([4, 5, 6]), @@ -104,7 +104,7 @@ main() { ]); }); - test("Empty 1", () { + test('Empty 1', () { testZip([ mks([]), mks([4, 5, 6]), @@ -112,7 +112,7 @@ main() { ], []); }); - test("Empty 2", () { + test('Empty 2', () { testZip([ mks([1, 2, 3]), mks([]), @@ -120,7 +120,7 @@ main() { ], []); }); - test("Empty 3", () { + test('Empty 3', () { testZip([ mks([1, 2, 3]), mks([4, 5, 6]), @@ -128,11 +128,11 @@ main() { ], []); }); - test("Empty source", () { + test('Empty source', () { testZip([], []); }); - test("Single Source", () { + test('Single Source', () { testZip([ mks([1, 2, 3]) ], [ @@ -142,12 +142,12 @@ main() { ]); }); - test("Other-streams", () { - Stream st1 = mks([1, 2, 3, 4, 5, 6]).where((x) => x < 4); + test('Other-streams', () { + var st1 = mks([1, 2, 3, 4, 5, 6]).where((x) => x < 4); Stream st2 = Stream.periodic(const Duration(milliseconds: 5), (x) => x + 4).take(3); - StreamController c = StreamController.broadcast(); - Stream st3 = c.stream; + var c = StreamController.broadcast(); + var st3 = c.stream; testZip([ st1, st2, @@ -164,67 +164,66 @@ main() { ..close(); }); - test("Error 1", () { + test('Error 1', () { expect( StreamZip([ - streamError(mks([1, 2, 3]), 2, "BAD-1"), + streamError(mks([1, 2, 3]), 2, 'BAD-1'), mks([4, 5, 6]), mks([7, 8, 9]) ]).toList(), - throwsA(equals("BAD-1"))); + throwsA(equals('BAD-1'))); }); - test("Error 2", () { + test('Error 2', () { expect( StreamZip([ mks([1, 2, 3]), - streamError(mks([4, 5, 6]), 5, "BAD-2"), + streamError(mks([4, 5, 6]), 5, 'BAD-2'), mks([7, 8, 9]) ]).toList(), - throwsA(equals("BAD-2"))); + throwsA(equals('BAD-2'))); }); - test("Error 3", () { + test('Error 3', () { expect( StreamZip([ mks([1, 2, 3]), mks([4, 5, 6]), - streamError(mks([7, 8, 9]), 8, "BAD-3") + streamError(mks([7, 8, 9]), 8, 'BAD-3') ]).toList(), - throwsA(equals("BAD-3"))); + throwsA(equals('BAD-3'))); }); - test("Error at end", () { + test('Error at end', () { expect( StreamZip([ mks([1, 2, 3]), - streamError(mks([4, 5, 6]), 6, "BAD-4"), + streamError(mks([4, 5, 6]), 6, 'BAD-4'), mks([7, 8, 9]) ]).toList(), - throwsA(equals("BAD-4"))); + throwsA(equals('BAD-4'))); }); - test("Error before first end", () { + test('Error before first end', () { // StreamControllers' streams with no "close" called will never be done, // so the fourth event of the first stream is guaranteed to come first. expect( StreamZip([ - streamError(mks([1, 2, 3, 4]), 4, "BAD-5"), + streamError(mks([1, 2, 3, 4]), 4, 'BAD-5'), (StreamController()..add(4)..add(5)..add(6)).stream, (StreamController()..add(7)..add(8)..add(9)).stream ]).toList(), - throwsA(equals("BAD-5"))); + throwsA(equals('BAD-5'))); }); - test("Error after first end", () { - StreamController controller = StreamController(); + test('Error after first end', () { + var controller = StreamController(); controller..add(7)..add(8)..add(9); // Transformer that puts error into controller when one of the first two // streams have sent a done event. - StreamTransformer trans = - StreamTransformer.fromHandlers(handleDone: (EventSink s) { + var trans = StreamTransformer.fromHandlers(handleDone: (EventSink s) { Timer.run(() { - controller.addError("BAD-6"); + controller.addError('BAD-6'); }); s.close(); }); @@ -239,16 +238,16 @@ main() { ]); }); - test("Pause/Resume", () { - int sc1p = 0; - StreamController c1 = StreamController(onPause: () { + test('Pause/Resume', () { + var sc1p = 0; + var c1 = StreamController(onPause: () { sc1p++; }, onResume: () { sc1p--; }); - int sc2p = 0; - StreamController c2 = StreamController(onPause: () { + var sc2p = 0; + var c2 = StreamController(onPause: () { sc2p++; }, onResume: () { sc2p--; @@ -264,7 +263,7 @@ main() { const ms25 = Duration(milliseconds: 25); // StreamIterator uses pause and resume to control flow. - StreamIterator it = StreamIterator(zip); + var it = StreamIterator(zip); it.moveNext().then((hasMore) { expect(hasMore, isTrue); @@ -301,11 +300,11 @@ main() { c2..add(2)..add(4); }); - test("pause-resume2", () { + test('pause-resume2', () { var s1 = Stream.fromIterable([0, 2, 4, 6, 8]); var s2 = Stream.fromIterable([1, 3, 5, 7]); var sz = StreamZip([s1, s2]); - int ctr = 0; + var ctr = 0; StreamSubscription sub; sub = sz.listen(expectAsync1((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index c7756e60..68cd7235 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -2,36 +2,37 @@ // 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. -import "dart:async"; -import "package:test/test.dart"; +import 'dart:async'; + +import 'package:test/test.dart'; // Test that stream listener callbacks all happen in the zone where the // listen occurred. -main() { +void main() { StreamController controller; controller = StreamController(); - testStream("singlesub-async", controller, controller.stream); + testStream('singlesub-async', controller, controller.stream); controller = StreamController.broadcast(); - testStream("broadcast-async", controller, controller.stream); + testStream('broadcast-async', controller, controller.stream); controller = StreamController(); testStream( - "asbroadcast-async", controller, controller.stream.asBroadcastStream()); + 'asbroadcast-async', controller, controller.stream.asBroadcastStream()); controller = StreamController(sync: true); - testStream("singlesub-sync", controller, controller.stream); + testStream('singlesub-sync', controller, controller.stream); controller = StreamController.broadcast(sync: true); - testStream("broadcast-sync", controller, controller.stream); + testStream('broadcast-sync', controller, controller.stream); controller = StreamController(sync: true); testStream( - "asbroadcast-sync", controller, controller.stream.asBroadcastStream()); + 'asbroadcast-sync', controller, controller.stream.asBroadcastStream()); } void testStream(String name, StreamController controller, Stream stream) { test(name, () { - Zone outer = Zone.current; + var outer = Zone.current; runZoned(() { - Zone newZone1 = Zone.current; + var newZone1 = Zone.current; StreamSubscription sub; sub = stream.listen(expectAsync1((v) { expect(v, 42); diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 9460dec5..0b24a022 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -2,15 +2,15 @@ // 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. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart" show SubscriptionStream; -import "package:test/test.dart"; +import 'package:async/async.dart' show SubscriptionStream; +import 'package:test/test.dart'; -import "utils.dart"; +import 'utils.dart'; -main() { - test("subscription stream of an entire subscription", () async { +void main() { + test('subscription stream of an entire subscription', () async { var stream = createStream(); var subscription = stream.listen(null); var subscriptionStream = SubscriptionStream(subscription); @@ -18,7 +18,7 @@ main() { expect(subscriptionStream.toList(), completion([1, 2, 3, 4])); }); - test("subscription stream after two events", () async { + test('subscription stream after two events', () async { var stream = createStream(); var skips = 0; var completer = Completer(); @@ -35,7 +35,7 @@ main() { expect(subscriptionStream.toList(), completion([3, 4])); }); - test("listening twice fails", () async { + test('listening twice fails', () async { var stream = createStream(); var sourceSubscription = stream.listen(null); var subscriptionStream = SubscriptionStream(sourceSubscription); @@ -44,7 +44,7 @@ main() { await subscription.cancel(); }); - test("pause and cancel passed through to original stream", () async { + test('pause and cancel passed through to original stream', () async { var controller = StreamController(onCancel: () async => 42); var sourceSubscription = controller.stream.listen(null); var subscriptionStream = SubscriptionStream(sourceSubscription); @@ -69,9 +69,9 @@ main() { expect(controller.hasListener, isFalse); }); - group("cancelOnError source:", () { + group('cancelOnError source:', () { for (var sourceCancels in [false, true]) { - group("${sourceCancels ? "yes" : "no"}:", () { + group('${sourceCancels ? "yes" : "no"}:', () { SubscriptionStream subscriptionStream; Future onCancel; // Completes if source stream is canceled before done. setUp(() { @@ -83,16 +83,16 @@ main() { subscriptionStream = SubscriptionStream(sourceSubscription); }); - test("- subscriptionStream: no", () async { + test('- subscriptionStream: no', () async { var done = Completer(); var events = []; subscriptionStream.listen(events.add, onError: events.add, onDone: done.complete, cancelOnError: false); - var expected = [1, 2, "To err is divine!"]; + var expected = [1, 2, 'To err is divine!']; if (sourceCancels) { await onCancel; // And [done] won't complete at all. - bool isDone = false; + var isDone = false; done.future.then((_) { isDone = true; }); @@ -105,7 +105,7 @@ main() { expect(events, expected); }); - test("- subscriptionStream: yes", () async { + test('- subscriptionStream: yes', () async { var completer = Completer(); var events = []; subscriptionStream.listen(events.add, @@ -113,18 +113,18 @@ main() { events.add(value); completer.complete(); }, - onDone: () => throw "should not happen", + onDone: () => throw 'should not happen', cancelOnError: true); await completer.future; await flushMicrotasks(); - expect(events, [1, 2, "To err is divine!"]); + expect(events, [1, 2, 'To err is divine!']); }); }); } for (var cancelOnError in [false, true]) { - group(cancelOnError ? "yes" : "no", () { - test("- no error, value goes to asFuture", () async { + group(cancelOnError ? 'yes' : 'no', () { + test('- no error, value goes to asFuture', () async { var stream = createStream(); var sourceSubscription = stream.listen(null, cancelOnError: cancelOnError); @@ -134,7 +134,7 @@ main() { expect(subscription.asFuture(42), completion(42)); }); - test("- error goes to asFuture", () async { + test('- error goes to asFuture', () async { var stream = createErrorStream(); var sourceSubscription = stream.listen(null, cancelOnError: cancelOnError); @@ -160,13 +160,13 @@ Stream createStream() async* { } Stream createErrorStream([Completer onCancel]) async* { - bool canceled = true; + var canceled = true; try { yield 1; await flushMicrotasks(); yield 2; await flushMicrotasks(); - yield* Future.error("To err is divine!").asStream(); + yield* Future.error('To err is divine!').asStream(); await flushMicrotasks(); yield 4; await flushMicrotasks(); @@ -181,5 +181,7 @@ Stream createErrorStream([Completer onCancel]) async* { } Stream createLongStream() async* { - for (int i = 0; i < 200; i++) yield i; + for (var i = 0; i < 200; i++) { + yield i; + } } diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index 64f45ec7..f0e3a701 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -10,8 +10,8 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - group("with no callbacks", () { - test("forwards cancellation", () async { + group('with no callbacks', () { + test('forwards cancellation', () async { var isCanceled = false; var cancelCompleter = Completer(); var controller = StreamController(onCancel: expectAsync0(() { @@ -39,7 +39,7 @@ void main() { expect(subscription.cancel(), completes); }); - test("forwards pausing and resuming", () async { + test('forwards pausing and resuming', () async { var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer()) @@ -62,7 +62,7 @@ void main() { expect(controller.isPaused, isFalse); }); - test("forwards pausing with a resume future", () async { + test('forwards pausing with a resume future', () async { var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer()) @@ -79,8 +79,8 @@ void main() { }); }); - group("with a cancel callback", () { - test("invokes the callback when the subscription is canceled", () async { + group('with a cancel callback', () { + test('invokes the callback when the subscription is canceled', () async { var isCanceled = false; var callbackInvoked = false; var controller = StreamController(onCancel: expectAsync0(() { @@ -103,7 +103,7 @@ void main() { expect(isCanceled, isTrue); }); - test("invokes the callback once and caches its result", () async { + test('invokes the callback once and caches its result', () async { var completer = Completer(); var controller = StreamController(); var subscription = controller.stream @@ -132,8 +132,8 @@ void main() { }); }); - group("with a pause callback", () { - test("invokes the callback when pause is called", () async { + group('with a pause callback', () { + test('invokes the callback when pause is called', () async { var pauseCount = 0; var controller = StreamController(); var subscription = controller.stream @@ -180,8 +180,8 @@ void main() { }); }); - group("with a resume callback", () { - test("invokes the callback when resume is called", () async { + group('with a resume callback', () { + test('invokes the callback when resume is called', () async { var resumeCount = 0; var controller = StreamController(); var subscription = controller.stream @@ -213,7 +213,7 @@ void main() { expect(resumeCount, equals(3)); }); - test("invokes the callback when a resume future completes", () async { + test('invokes the callback when a resume future completes', () async { var resumed = false; var controller = StreamController(); var subscription = controller.stream.transform( @@ -247,7 +247,7 @@ void main() { }); }); - group("when the outer subscription is canceled but the inner is not", () { + group('when the outer subscription is canceled but the inner is not', () { StreamSubscription subscription; setUp(() { var controller = StreamController(); @@ -258,7 +258,7 @@ void main() { onDone: expectAsync0(() {}, count: 0)); subscription.cancel(); controller.add(1); - controller.addError("oh no!"); + controller.addError('oh no!'); controller.close(); }); @@ -277,11 +277,11 @@ void main() { await flushMicrotasks(); }); - test("isPaused returns false", () { + test('isPaused returns false', () { expect(subscription.isPaused, isFalse); }); - test("asFuture never completes", () async { + test('asFuture never completes', () async { subscription.asFuture().then(expectAsync1((_) {}, count: 0)); await flushMicrotasks(); }); diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index bbec8f7e..2a115463 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -4,13 +4,13 @@ import 'dart:async'; -import "package:async/src/typed/stream_subscription.dart"; -import "package:test/test.dart"; +import 'package:async/src/typed/stream_subscription.dart'; +import 'package:test/test.dart'; import '../utils.dart'; void main() { - group("with valid types, forwards", () { + group('with valid types, forwards', () { StreamController controller; StreamSubscription wrapper; bool isCanceled; @@ -21,26 +21,26 @@ void main() { wrapper = TypeSafeStreamSubscription(controller.stream.listen(null)); }); - test("onData()", () { + test('onData()', () { wrapper.onData(expectAsync1((data) { expect(data, equals(1)); })); controller.add(1); }); - test("onError()", () { + test('onError()', () { wrapper.onError(expectAsync1((error) { - expect(error, equals("oh no")); + expect(error, equals('oh no')); })); - controller.addError("oh no"); + controller.addError('oh no'); }); - test("onDone()", () { + test('onDone()', () { wrapper.onDone(expectAsync0(() {})); controller.close(); }); - test("pause(), resume(), and isPaused", () async { + test('pause(), resume(), and isPaused', () async { expect(wrapper.isPaused, isFalse); wrapper.pause(); @@ -54,19 +54,19 @@ void main() { expect(wrapper.isPaused, isFalse); }); - test("cancel()", () async { + test('cancel()', () async { wrapper.cancel(); await flushMicrotasks(); expect(isCanceled, isTrue); }); - test("asFuture()", () { + test('asFuture()', () { expect(wrapper.asFuture(12), completion(equals(12))); controller.close(); }); }); - group("with invalid types,", () { + group('with invalid types,', () { StreamController controller; StreamSubscription wrapper; bool isCanceled; @@ -77,8 +77,8 @@ void main() { wrapper = TypeSafeStreamSubscription(controller.stream.listen(null)); }); - group("throws a CastError for", () { - test("onData()", () { + group('throws a CastError for', () { + test('onData()', () { expect(() { // TODO(nweiz): Use the wrapper declared in setUp when sdk#26226 is // fixed. @@ -87,28 +87,28 @@ void main() { TypeSafeStreamSubscription(controller.stream.listen(null)); wrapper.onData(expectAsync1((_) {}, count: 0)); - controller.add("foo"); + controller.add('foo'); }, throwsZonedCastError); }); }); group("doesn't throw a CastError for", () { - test("onError()", () { + test('onError()', () { wrapper.onError(expectAsync1((error) { - expect(error, equals("oh no")); + expect(error, equals('oh no')); })); - controller.add("foo"); - controller.addError("oh no"); + controller.add('foo'); + controller.addError('oh no'); }); - test("onDone()", () { + test('onDone()', () { wrapper.onDone(expectAsync0(() {})); - controller.add("foo"); + controller.add('foo'); controller.close(); }); - test("pause(), resume(), and isPaused", () async { - controller.add("foo"); + test('pause(), resume(), and isPaused', () async { + controller.add('foo'); expect(wrapper.isPaused, isFalse); @@ -123,17 +123,17 @@ void main() { expect(wrapper.isPaused, isFalse); }); - test("cancel()", () async { - controller.add("foo"); + test('cancel()', () async { + controller.add('foo'); wrapper.cancel(); await flushMicrotasks(); expect(isCanceled, isTrue); }); - test("asFuture()", () { + test('asFuture()', () { expect(wrapper.asFuture(12), completion(equals(12))); - controller.add("foo"); + controller.add('foo'); controller.close(); }); }); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index e517c78a..b09d06b5 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -3,10 +3,10 @@ // BSD-style license that can be found in the LICENSE file. /// Helper utilities for testing. -import "dart:async"; +import 'dart:async'; -import "package:async/async.dart"; -import "package:test/test.dart"; +import 'package:async/async.dart'; +import 'package:test/test.dart'; /// A zero-millisecond timer should wait until after all microtasks. Future flushMicrotasks() => Future.delayed(Duration.zero); @@ -17,7 +17,7 @@ typedef OptionalArgAction = void Function([dynamic a, dynamic b]); /// /// Returns a function that fails the test if it is ever called. OptionalArgAction unreachable(String name) => - ([a, b]) => fail("Unreachable: $name"); + ([a, b]) => fail('Unreachable: $name'); // TODO(nweiz): Use the version of this in test when test#418 is fixed. /// A matcher that runs a callback in its own zone and asserts that that zone @@ -46,9 +46,10 @@ final throwsCastError = throwsA(TypeMatcher()); /// A badly behaved stream which throws if it's ever listened to. /// /// Can be used to test cases where a stream should not be used. -class UnusableStream extends Stream { - listen(onData, {onError, onDone, cancelOnError}) { - throw UnimplementedError("Gotcha!"); +class UnusableStream extends Stream { + @override + StreamSubscription listen(onData, {onError, onDone, cancelOnError}) { + throw UnimplementedError('Gotcha!'); } } @@ -60,11 +61,16 @@ class UnusableStream extends Stream { class CompleterStreamSink implements StreamSink { final completer = Completer(); + @override Future get done => completer.future; + @override void add(T event) {} + @override void addError(error, [StackTrace stackTrace]) {} + @override Future addStream(Stream stream) async {} + @override Future close() => completer.future; } @@ -79,6 +85,7 @@ class TestSink implements StreamSink { bool get isClosed => _isClosed; var _isClosed = false; + @override Future get done => _doneCompleter.future; final _doneCompleter = Completer(); @@ -88,22 +95,26 @@ class TestSink implements StreamSink { /// /// If [onDone] is passed, it's called when the user calls [close]. Its result /// is piped to the [done] future. - TestSink({onDone()}) : _onDone = onDone ?? (() {}); + TestSink({void Function() onDone}) : _onDone = onDone ?? (() {}); + @override void add(T event) { results.add(Result.value(event)); } + @override void addError(error, [StackTrace stackTrace]) { results.add(Result.error(error, stackTrace)); } + @override Future addStream(Stream stream) { var completer = Completer.sync(); stream.listen(add, onError: addError, onDone: completer.complete); return completer.future; } + @override Future close() { _isClosed = true; _doneCompleter.complete(Future.microtask(_onDone)); From e4cb9a7423e7b4f1c401c884462a237650172825 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 9 Dec 2019 14:06:32 -0800 Subject: [PATCH 142/260] Drop unused author field from pubspec (dart-lang/async#93) --- pkgs/async/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e4722039..d015b9fc 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -2,7 +2,6 @@ name: async version: 2.4.1-dev description: Utility functions and classes related to the 'dart:async' library. -author: Dart Team homepage: https://www.github.com/dart-lang/async environment: From ef728c423c6e43fb58f60b763ffabdcb55e914ae Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 14 Jan 2020 11:48:01 -0800 Subject: [PATCH 143/260] Drop optional new from docs (dart-lang/async#94) - Remove `new` keyword in code samples. - Remove `new` keyword in doc reference to named constructors. --- pkgs/async/CHANGELOG.md | 2 ++ pkgs/async/lib/src/async_memoizer.dart | 2 +- pkgs/async/lib/src/null_stream_sink.dart | 6 +++--- pkgs/async/lib/src/stream_group.dart | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 9071526a..da244b5e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.4.1-dev + ## 2.4.0 * Add `StreamGroup.mergeBroadcast()` utility. diff --git a/pkgs/async/lib/src/async_memoizer.dart b/pkgs/async/lib/src/async_memoizer.dart index 7e545fe2..c05c9275 100644 --- a/pkgs/async/lib/src/async_memoizer.dart +++ b/pkgs/async/lib/src/async_memoizer.dart @@ -19,7 +19,7 @@ import 'dart:async'; /// /// ```dart /// class SomeResource { -/// final _closeMemo = new AsyncMemoizer(); +/// final _closeMemo = AsyncMemoizer(); /// /// Future close() => _closeMemo.runOnce(() { /// // ... diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart index f820328c..b28df2a6 100644 --- a/pkgs/async/lib/src/null_stream_sink.dart +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -12,16 +12,16 @@ import 'dart:async'; /// been closed. /// /// This can be used when a sink is needed but no events are actually intended -/// to be added. The [new NullStreamSink.error] constructor can be used to +/// to be added. The [NullStreamSink.error] constructor can be used to /// represent errors when creating a sink, since [StreamSink.done] exposes sink /// errors. For example: /// /// ```dart /// StreamSink> openForWrite(String filename) { /// try { -/// return new RandomAccessSink(new File(filename).openSync()); +/// return RandomAccessSink(File(filename).openSync()); /// } on IOException catch (error, stackTrace) { -/// return new NullStreamSink.error(error, stackTrace); +/// return NullStreamSink.error(error, stackTrace); /// } /// } /// ``` diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 8ea95f3e..e9a4afc3 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -16,7 +16,7 @@ import 'dart:async'; /// be single-subscription. In this case, if [stream] is paused or canceled, all /// streams in the group will likewise be paused or canceled, respectively. /// -/// If the `StreamGroup` is constructed using [new StreamGroup.broadcast], +/// If the `StreamGroup` is constructed using [StreamGroup.broadcast], /// [stream] will be a broadcast stream. In this case, the streams in the group /// will never be paused and single-subscription streams in the group will never /// be canceled. **Note that single-subscription streams in a broadcast group From 2896d7f32d619a13dec27914da5ff9017d72f749 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 14 Jan 2020 12:57:14 -0800 Subject: [PATCH 144/260] Use a Completer of void (dart-lang/async#96) Resolves a TODO comment. --- pkgs/async/lib/src/stream_queue.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index cf75a6d5..97d74c73 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -848,8 +848,7 @@ class _LookAheadRequest extends _ListRequest { /// source subscription. class _CancelRequest implements _EventRequest { /// Completer for the future returned by the `cancel` call. - /// TODO(lrn); make this Completer when that is implemented. - final _completer = Completer(); + final _completer = Completer(); /// When the event is completed, it needs to cancel the active subscription /// of the `StreamQueue` object, if any. From eb18edc93ef4d285291604f03319ee2b83bd7723 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 14 Jan 2020 15:11:45 -0800 Subject: [PATCH 145/260] Fix implicit casts (dart-lang/async#97) - Add an explicit `StackTrace` on arguments to `onError` callback arguments since they are statically `Function` so no type inference flows into argument types. - Add a couple explicit casts. - Add explicit types on some completers in test code. - Use `Queue.of` constructor to allow type inference to flow. - Fix a broken argument type on `DelegatingFuture`. - Add some missing generic type arguments on fields or arguments. --- pkgs/async/analysis_options.yaml | 2 ++ pkgs/async/lib/src/cancelable_operation.dart | 13 +++++++------ pkgs/async/lib/src/delegate/future.dart | 3 ++- pkgs/async/lib/src/future_group.dart | 2 +- pkgs/async/lib/src/result/release_sink.dart | 4 ++-- pkgs/async/lib/src/result/result.dart | 5 +++-- pkgs/async/lib/src/stream_sink_completer.dart | 2 +- pkgs/async/test/result/result_captureAll_test.dart | 2 +- pkgs/async/test/result/result_flattenAll_test.dart | 4 ++-- pkgs/async/test/result/result_test.dart | 12 ++++++------ pkgs/async/test/stream_completer_test.dart | 4 ++-- pkgs/async/test/stream_zip_test.dart | 7 ++++--- pkgs/async/test/utils.dart | 6 +++--- 13 files changed, 36 insertions(+), 30 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index f2176979..1f502a53 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,5 +1,7 @@ include: package:pedantic/analysis_options.yaml analyzer: + strong-mode: + implicit-casts: false errors: todo: ignore # Lint provided by pkg:pedantic – should fix this! diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index bac8f9b2..cbbb8d0f 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -50,7 +50,7 @@ class CancelableOperation { value.then((value) { controller.add(value); controller.close(); - }, onError: (error, stackTrace) { + }, onError: (error, StackTrace stackTrace) { controller.addError(error, stackTrace); controller.close(); }); @@ -104,7 +104,7 @@ class CancelableOperation { completer._cancel(); } } - }, onError: (error, stackTrace) { + }, onError: (error, StackTrace stackTrace) { if (!completer.isCanceled) { if (onError != null) { completer.complete(Future.sync(() => onError(error, stackTrace))); @@ -170,7 +170,7 @@ class CancelableCompleter { /// /// If [value] is a [Future], this will complete to the result of that /// [Future] once it completes. - void complete([value]) { + void complete([FutureOr value]) { if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; @@ -180,16 +180,17 @@ class CancelableCompleter { return; } + final future = value as Future; if (_isCanceled) { // Make sure errors from [value] aren't top-leveled. - value.catchError((_) {}); + future.catchError((_) {}); return; } - value.then((result) { + future.then((result) { if (_isCanceled) return; _inner.complete(result); - }, onError: (error, stackTrace) { + }, onError: (error, StackTrace stackTrace) { if (_isCanceled) return; _inner.completeError(error, stackTrace); }); diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 8c5dd043..984caf6d 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -33,7 +33,8 @@ class DelegatingFuture implements Future { _future.then(onValue, onError: onError); @override - Future whenComplete(FutureOr action) => _future.whenComplete(action); + Future whenComplete(FutureOr Function() action) => + _future.whenComplete(action); @override Future timeout(Duration timeLimit, {FutureOr Function() onTimeout}) => diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index a6abff09..402ae46a 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -76,7 +76,7 @@ class FutureGroup implements Sink> { if (!_closed) return null; if (_onIdleController != null) _onIdleController.close(); _completer.complete(_values); - }).catchError((error, stackTrace) { + }).catchError((error, StackTrace stackTrace) { if (_completer.isCompleted) return null; _completer.completeError(error, stackTrace); }); diff --git a/pkgs/async/lib/src/result/release_sink.dart b/pkgs/async/lib/src/result/release_sink.dart index 78d0f426..5d8267a1 100644 --- a/pkgs/async/lib/src/result/release_sink.dart +++ b/pkgs/async/lib/src/result/release_sink.dart @@ -8,9 +8,9 @@ import 'result.dart'; /// Used by [Result.releaseSink]. class ReleaseSink implements EventSink> { - final EventSink _sink; + final EventSink _sink; - ReleaseSink(EventSink sink) : _sink = sink; + ReleaseSink(this._sink); @override void add(Result result) { diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index e04da9f5..e6047822 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -85,7 +85,8 @@ abstract class Result { /// Errors have been converted to an [ErrorResult] value. static Future> capture(Future future) { return future.then((value) => ValueResult(value), - onError: (error, stackTrace) => ErrorResult(error, stackTrace)); + onError: (error, StackTrace stackTrace) => + ErrorResult(error, stackTrace)); } /// Captures each future in [elements], @@ -111,7 +112,7 @@ abstract class Result { } }); } else { - results.add(Result.value(element)); + results.add(Result.value(element as T)); } } if (pending == 0) { diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index 3b315762..ebfd717d 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -27,7 +27,7 @@ class StreamSinkCompleter { final StreamSink sink = _CompleterSink(); /// Returns [sink] typed as a [_CompleterSink]. - _CompleterSink get _sink => sink; + _CompleterSink get _sink => sink as _CompleterSink; /// Convert a `Future` to a `StreamSink`. /// diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index ba8305a1..8e798720 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -138,7 +138,7 @@ void main() { var all = Result.captureAll(cs.map((c) => c.future)); var rnd = Random(seed); var throwFlags = rnd.nextInt(1 << n); // Bit-flag for throwing. - bool throws(index) => (throwFlags & (1 << index)) != 0; + bool throws(int index) => (throwFlags & (1 << index)) != 0; var expected = List.generate(n, (x) => throws(x) ? err(x) : res(x)); expect(all, completion(expected)); diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart index 890b2d02..c0a86035 100644 --- a/pkgs/async/test/result/result_flattenAll_test.dart +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; final someStack = StackTrace.current; Result res(T n) => Result.value(n); -Result err(n) => ErrorResult('$n', someStack); +Result err(n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of results. Iterable> results(int count, @@ -22,7 +22,7 @@ Iterable> results(int count, } void main() { - void expectAll(result, expectation) { + void expectAll(Result result, Result expectation) { if (expectation.isError) { expect(result, expectation); } else { diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index 92fc6b08..adbc4457 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -173,7 +173,7 @@ void main() { test('capture stream', () { var c = StreamController(); var stream = Result.captureStream(c.stream); - var expectedList = Queue.from( + var expectedList = Queue.of( [Result.value(42), Result.error('BAD', stack), Result.value(37)]); void listener(Result actual) { expect(expectedList.isEmpty, isFalse); @@ -197,7 +197,7 @@ void main() { Result.value(37) ]; // Expect the data events, and an extra error event. - var expectedList = Queue.from(events)..add(Result.error('BAD2', stack)); + var expectedList = Queue.of(events)..add(Result.error('BAD2', stack)); void dataListener(int v) { expect(expectedList.isEmpty, isFalse); @@ -260,7 +260,7 @@ void main() { }); test('handle unary', () { - ErrorResult result = Result.error('error', stack); + var result = ErrorResult('error', stack); var called = false; result.handle((error) { called = true; @@ -270,7 +270,7 @@ void main() { }); test('handle binary', () { - ErrorResult result = Result.error('error', stack); + var result = ErrorResult('error', stack); var called = false; result.handle((error, stackTrace) { called = true; @@ -281,7 +281,7 @@ void main() { }); test('handle unary and binary', () { - ErrorResult result = Result.error('error', stack); + var result = ErrorResult('error', stack); var called = false; result.handle((error, [stackTrace]) { called = true; @@ -292,7 +292,7 @@ void main() { }); test('handle neither unary nor binary', () { - ErrorResult result = Result.error('error', stack); + var result = ErrorResult('error', stack); expect(() => result.handle(() => fail('unreachable')), throwsA(anything)); expect(() => result.handle((a, b, c) => fail('unreachable')), throwsA(anything)); diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 2cf6728a..0cd210ac 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -74,9 +74,9 @@ void main() { }); test('cancel new stream before source is done', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); var lastEvent = -1; - var controller = StreamController(); + var controller = StreamController(); StreamSubscription subscription; subscription = completer.stream.listen((value) { expect(value, lessThan(3)); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index f462996d..19c3decb 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -15,9 +15,9 @@ Stream streamError(Stream base, int errorValue, error) { /// Make a [Stream] from an [Iterable] by adding events to a stream controller /// at periodic intervals. -Stream mks(Iterable iterable) { +Stream mks(Iterable iterable) { var iterator = iterable.iterator; - var controller = StreamController(); + var controller = StreamController(); // Some varying time between 3 and 10 ms. var ms = ((++ctr) * 5) % 7 + 3; Timer.periodic(Duration(milliseconds: ms), (Timer timer) { @@ -221,7 +221,8 @@ void main() { controller..add(7)..add(8)..add(9); // Transformer that puts error into controller when one of the first two // streams have sent a done event. - var trans = StreamTransformer.fromHandlers(handleDone: (EventSink s) { + var trans = + StreamTransformer.fromHandlers(handleDone: (EventSink s) { Timer.run(() { controller.addError('BAD-6'); }); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index b09d06b5..a99fe9b1 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -22,10 +22,10 @@ OptionalArgAction unreachable(String name) => // TODO(nweiz): Use the version of this in test when test#418 is fixed. /// A matcher that runs a callback in its own zone and asserts that that zone /// emits an error that matches [matcher]. -Matcher throwsZoned(matcher) => predicate((callback) { +Matcher throwsZoned(matcher) => predicate((void Function() callback) { var firstError = true; runZoned(callback, - onError: expectAsync2((error, stackTrace) { + onError: expectAsync2((error, StackTrace stackTrace) { if (firstError) { expect(error, matcher); firstError = false; @@ -89,7 +89,7 @@ class TestSink implements StreamSink { Future get done => _doneCompleter.future; final _doneCompleter = Completer(); - final Function _onDone; + final void Function() _onDone; /// Creates a new sink. /// From b922233be7bfe381c596b14d8be87be76f812408 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 14 Jan 2020 15:30:27 -0800 Subject: [PATCH 146/260] Remove a stale TODO (dart-lang/async#100) Dart 2 provides strong guarantees that the reified type of an object will be assignable to the static type, this isn't something we need a runtime test for ourselves. --- pkgs/async/test/stream_splitter_test.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index fd37f87c..3748c5ce 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -97,10 +97,6 @@ void main() { expect(() => branch.listen(null), throwsStateError); }); - // TODO(nweiz): Test that branches have the correct reified type once Dart - // 1.11 is released. In 1.10, the stream exposed by a StreamController didn't - // have a reified type. - test('multiple branches each replay the stream', () async { var branch1 = splitter.split(); controller.add(1); From 7b6f9bc242411f1cbc4836c26bfd9e38e2b957fe Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 14 Jan 2020 15:30:43 -0800 Subject: [PATCH 147/260] Deprecate Delegating*.typed utilities (dart-lang/async#99) Most of these have simpler or safer versions in Dart 2. The "Sink" classes have a workaround which is ugly, but these also had nearly no usage. --- pkgs/async/CHANGELOG.md | 11 +++++++++++ pkgs/async/README.md | 5 ----- pkgs/async/lib/src/delegate/event_sink.dart | 2 ++ pkgs/async/lib/src/delegate/sink.dart | 2 ++ pkgs/async/lib/src/delegate/stream.dart | 1 + pkgs/async/lib/src/delegate/stream_consumer.dart | 2 ++ pkgs/async/lib/src/delegate/stream_sink.dart | 2 ++ pkgs/async/lib/src/delegate/stream_subscription.dart | 2 ++ pkgs/async/lib/src/lazy_stream.dart | 7 ++----- pkgs/async/lib/src/stream_sink_transformer.dart | 2 ++ pkgs/async/lib/src/stream_sink_transformer/typed.dart | 5 ++--- pkgs/async/lib/src/typed_stream_transformer.dart | 6 ++---- 12 files changed, 30 insertions(+), 17 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index da244b5e..1de25815 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,16 @@ ## 2.4.1-dev +* Deprecate `DelegatingStream.typed`. Use `Stream.cast` instead. +* Deprecate `DelegatingStreamSubcription.typed` and + `DelegatingStreamConsumer.typed`. For each of these the `Stream` should be + cast to the correct type before being used. +* Deprecate `DelegatingStreamSink.typed`. `DelegatingSink.typed`, + `DelegatingEventSink.typed`, `DelegatingStreamConsumer.typed`. For each of + these a new `StreamController` can be constructed to forward to the sink. + `StreamController()..stream.cast().pipe(sink)` +* Deprecate `typedStreamTransformer`. Cast after transforming instead. +* Deprecate `StreamSinkTransformer.typed` since there was no usage. + ## 2.4.0 * Add `StreamGroup.mergeBroadcast()` utility. diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 6e2b8dec..359b93a1 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -22,11 +22,6 @@ computations. [`DelegatingEventSink`][DelegatingEventSink], and [`DelegatingStreamSink`][DelegatingStreamSink]. - The delegating classes all have `.typed()` constructors which allow users to - cast the generic type parameters in a way that's safe for strong mode. For - example, if `future` is a `Future` and you know it actually contains an - `int`, you can write `DelegatingFuture.typed(future)`. - * The [`FutureGroup`][FutureGroup] class makes it easy to wait until a group of features that may change over time completes. diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 977ee643..bc33b193 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -22,6 +22,8 @@ class DelegatingEventSink implements EventSink { /// instance of `EventSink`, not `EventSink`. This means that calls to /// [add] may throw a [CastError] if the argument type doesn't match the /// reified type of [sink]. + @Deprecated( + 'Use StreamController(sync: true)..stream.cast().pipe(sink)') static EventSink typed(EventSink sink) => sink is EventSink ? sink : DelegatingEventSink._(sink); diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index c1e01d71..57ef2a0d 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -20,6 +20,8 @@ class DelegatingSink implements Sink { /// instance of `Sink`, not `Sink`. This means that calls to [add] may /// throw a [CastError] if the argument type doesn't match the reified type of /// [sink]. + @Deprecated( + 'Use StreamController(sync: true)..stream.cast().pipe(sink)') static Sink typed(Sink sink) => sink is Sink ? sink : DelegatingSink._(sink); diff --git a/pkgs/async/lib/src/delegate/stream.dart b/pkgs/async/lib/src/delegate/stream.dart index 116d11f0..1585eab3 100644 --- a/pkgs/async/lib/src/delegate/stream.dart +++ b/pkgs/async/lib/src/delegate/stream.dart @@ -21,5 +21,6 @@ class DelegatingStream extends StreamView { /// original generic type, by asserting that its events are instances of `T` /// whenever they're provided. If they're not, the stream throws a /// [CastError]. + @Deprecated('Use stream.cast instead') static Stream typed(Stream stream) => stream.cast(); } diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index 591f9031..6b92006d 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -22,6 +22,8 @@ class DelegatingStreamConsumer implements StreamConsumer { /// instance of `StreamConsumer`, not `StreamConsumer`. This means that /// calls to [addStream] may throw a [CastError] if the argument type doesn't /// match the reified type of [consumer]. + @Deprecated( + 'Use StreamController(sync: true)..stream.cast().pipe(sink)') static StreamConsumer typed(StreamConsumer consumer) => consumer is StreamConsumer ? consumer diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index 39711896..9005d1d9 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -25,6 +25,8 @@ class DelegatingStreamSink implements StreamSink { /// of `StreamSink`, not `StreamSink`. This means that calls to [add] may /// throw a [CastError] if the argument type doesn't match the reified type of /// [sink]. + @Deprecated( + 'Use StreamController(sync: true)..stream.cast().pipe(sink)') static StreamSink typed(StreamSink sink) => sink is StreamSink ? sink : DelegatingStreamSink._(sink); diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 7d258a84..392e27bd 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -23,6 +23,8 @@ class DelegatingStreamSubscription implements StreamSubscription { /// regardless of its original generic type, by asserting that its events are /// instances of `T` whenever they're provided. If they're not, the /// subscription throws a [CastError]. + @Deprecated('Use Stream.cast instead') + // TODO - Remove `TypeSafeStreamSubscription` and tests when removing this. static StreamSubscription typed(StreamSubscription subscription) => subscription is StreamSubscription ? subscription diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index d2ac33c8..f5e65d13 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'delegate/stream.dart'; import 'stream_completer.dart'; import 'utils.dart'; @@ -40,11 +39,9 @@ class LazyStream extends Stream { Stream stream; if (result is Future>) { - stream = StreamCompleter.fromFuture(result.then((stream) { - return DelegatingStream.typed(stream); - })); + stream = StreamCompleter.fromFuture(result); } else { - stream = DelegatingStream.typed(result as Stream); + stream = result as Stream; } return stream.listen(onData, diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index c50cf17d..1dc27ed0 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -53,6 +53,8 @@ abstract class StreamSinkTransformer { /// This means that calls to [StreamSink.add] on the returned sink may throw a /// [CastError] if the argument type doesn't match the reified type of the /// sink. + @deprecated + // TODO remove TypeSafeStreamSinkTransformer static StreamSinkTransformer typed( StreamSinkTransformer transformer) => transformer is StreamSinkTransformer diff --git a/pkgs/async/lib/src/stream_sink_transformer/typed.dart b/pkgs/async/lib/src/stream_sink_transformer/typed.dart index c27bd240..4743c949 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/typed.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/typed.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import '../delegate/stream_sink.dart'; import '../stream_sink_transformer.dart'; /// A wrapper that coerces the generic type of the sink returned by an inner @@ -16,6 +15,6 @@ class TypeSafeStreamSinkTransformer TypeSafeStreamSinkTransformer(this._inner); @override - StreamSink bind(StreamSink sink) => - DelegatingStreamSink.typed(_inner.bind(sink)); + StreamSink bind(StreamSink sink) => StreamController(sync: true) + ..stream.cast().pipe(_inner.bind(sink)); } diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart index 23665054..c1af93f9 100644 --- a/pkgs/async/lib/src/typed_stream_transformer.dart +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -4,14 +4,13 @@ import 'dart:async'; -import 'delegate/stream.dart'; - /// Creates a wrapper that coerces the type of [transformer]. /// /// This soundly converts a [StreamTransformer] to a `StreamTransformer`, /// regardless of its original generic type, by asserting that the events /// emitted by the transformed stream are instances of `T` whenever they're /// provided. If they're not, the stream throws a [CastError]. +@Deprecated('Use Stream.cast after binding a transformer instead') StreamTransformer typedStreamTransformer( StreamTransformer transformer) => transformer is StreamTransformer @@ -26,6 +25,5 @@ class _TypeSafeStreamTransformer extends StreamTransformerBase { _TypeSafeStreamTransformer(this._inner); @override - Stream bind(Stream stream) => - DelegatingStream.typed(_inner.bind(stream)); + Stream bind(Stream stream) => _inner.bind(stream).cast(); } From 18a512c54cb65c6805b0fd90ed040d3d129f198d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 9 Mar 2020 13:04:45 -0700 Subject: [PATCH 148/260] Remove lints enabled by pkg:pedantic (dart-lang/async#103) --- pkgs/async/analysis_options.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 1f502a53..18c26e0a 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,4 +1,5 @@ include: package:pedantic/analysis_options.yaml + analyzer: strong-mode: implicit-casts: false @@ -6,9 +7,7 @@ analyzer: todo: ignore # Lint provided by pkg:pedantic – should fix this! unawaited_futures: ignore + linter: rules: - - prefer_generic_function_type_aliases - prefer_typing_uninitialized_variables - - unnecessary_const - - unnecessary_new From d7bb3e253735d57c9392c7c145e922ae122a4fe3 Mon Sep 17 00:00:00 2001 From: Leaf Petersen Date: Fri, 13 Mar 2020 15:54:42 -0700 Subject: [PATCH 149/260] Cast use of StreamSubscription.cancel result through dynamic (dart-lang/async#105) --- pkgs/async/test/subscription_stream_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 0b24a022..98a809f5 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -65,7 +65,7 @@ void main() { subscription.resume(); expect(controller.isPaused, isFalse); - expect(await subscription.cancel(), 42); + expect(await subscription.cancel() as dynamic, 42); expect(controller.hasListener, isFalse); }); From cc9e8420f29e6bd5bad362b79bef4d0123a6ef2e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 16 Mar 2020 12:38:01 -0700 Subject: [PATCH 150/260] Travis: do browser testing on Chrome (dart-lang/async#106) Hoping it's less flaky --- pkgs/async/.travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index ccc0864d..6fb2f8dd 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -6,9 +6,7 @@ dart: dart_task: - test: --platform vm - # No parallelism on Firefox (-j 1) - # Causes flakiness – need to investigate - - test: --platform firefox -j 1 + - test: --platform chrome - dartanalyzer - dartfmt From c1dda3d42159e01e3a7896ece493142d96107be0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 16 Mar 2020 13:30:22 -0700 Subject: [PATCH 151/260] Expand docs for isCompleted (dart-lang/async#104) Closes dart-lang/async#102 This field is confusing because it documents whether the backing completer has had `complete` called which doesn't necessarily correspond to when the value is available if it was completed with a `Future`. --- pkgs/async/lib/src/cancelable_operation.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index cbbb8d0f..9fd05347 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -29,6 +29,11 @@ class CancelableOperation { /// /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. + /// + /// Calling this constructor is equivalent to creating a [CancelableCompleter] + /// and completing it with [inner]. As such, [isCompleted] is true from the + /// moment this [CancelableOperation] is created, regardless of whether + /// [inner] has completed yet or not. factory CancelableOperation.fromFuture(Future inner, {FutureOr Function() onCancel}) { var completer = CancelableCompleter(onCancel: onCancel); @@ -125,7 +130,12 @@ class CancelableOperation { /// Whether this operation has been canceled before it completed. bool get isCanceled => _completer.isCanceled; - /// Whether this operation completed before being canceled. + /// Whether the [CancelableCompleter] backing this operation has been + /// completed. + /// + /// This value being true does not imply that the [value] future has + /// completed, but merely that it is no longer possible to [cancel] the + /// operation. bool get isCompleted => _completer.isCompleted; } From c76a8c2c3a2ad01d4be48f9760650a6387c309ed Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 16 Mar 2020 13:55:57 -0700 Subject: [PATCH 152/260] Prepare to publish (dart-lang/async#107) --- pkgs/async/CHANGELOG.md | 4 +++- pkgs/async/pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1de25815..46751148 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.4.1-dev +## 2.4.1 * Deprecate `DelegatingStream.typed`. Use `Stream.cast` instead. * Deprecate `DelegatingStreamSubcription.typed` and @@ -10,6 +10,8 @@ `StreamController()..stream.cast().pipe(sink)` * Deprecate `typedStreamTransformer`. Cast after transforming instead. * Deprecate `StreamSinkTransformer.typed` since there was no usage. +* Improve docs for `CancelablOperation.fromFuture`, indicate that `isCompleted` + starts `true`. ## 2.4.0 diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index d015b9fc..599ccdd5 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.4.1-dev +version: 2.4.1 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From f9378aaa6d68112f40e43695610b60cc7d7b69b6 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 30 Jun 2020 21:31:27 +0200 Subject: [PATCH 153/260] Make StreamQueue start listening immediately to broadcast streams. (dart-lang/async#119) When a `StreamQueue` is created for a broadcast stream, it didn't use to listen to the stream immediately, which means that any events sent before the first call to `next` (or any other queue request) would be silently lost. That makes an initial `peek` operation change the behavior of the queue, which is confusing to users. --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/stream_queue.dart | 8 +++++++- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 46751148..f247608b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.4.2 + +* `StreamQueue` starts listening immediately to broadcast strings. + ## 2.4.1 * Deprecate `DelegatingStream.typed`. Use `Stream.cast` instead. diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 97d74c73..278e249a 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -118,7 +118,13 @@ class StreamQueue { factory StreamQueue(Stream source) => StreamQueue._(source); // Private generative constructor to avoid subclasses. - StreamQueue._(this._source); + StreamQueue._(this._source) { + // Start listening immediately if we could otherwise lose events. + if (_source.isBroadcast) { + _ensureListening(); + _pause(); + } + } /// Asks if the stream has any more events. /// diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 599ccdd5..0cb71cf9 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.4.1 +version: 2.4.2 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From e3a6bd68cfce84001c87bce92b7f235889ea48f5 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Wed, 8 Jul 2020 08:57:51 -0700 Subject: [PATCH 154/260] Merge null_safety branch into master (dart-lang/async#125) Migrates this package to null safety and prepares for a null safety pre-release, once we have landed the package internally and in the SDK to verify its accuracy. --- pkgs/async/.travis.yml | 31 +++++-- pkgs/async/CHANGELOG.md | 4 + pkgs/async/analysis_options.yaml | 2 + pkgs/async/lib/src/async_cache.dart | 12 +-- pkgs/async/lib/src/cancelable_operation.dart | 37 +++++---- pkgs/async/lib/src/delegate/event_sink.dart | 2 +- pkgs/async/lib/src/delegate/future.dart | 6 +- pkgs/async/lib/src/delegate/stream_sink.dart | 2 +- .../lib/src/delegate/stream_subscription.dart | 10 +-- pkgs/async/lib/src/future_group.dart | 15 ++-- pkgs/async/lib/src/lazy_stream.dart | 12 +-- pkgs/async/lib/src/null_stream_sink.dart | 8 +- pkgs/async/lib/src/result/capture_sink.dart | 2 +- pkgs/async/lib/src/result/error.dart | 10 ++- pkgs/async/lib/src/result/future.dart | 4 +- pkgs/async/lib/src/result/release_sink.dart | 2 +- pkgs/async/lib/src/result/result.dart | 28 +++---- pkgs/async/lib/src/result/value.dart | 2 +- .../src/single_subscription_transformer.dart | 6 +- pkgs/async/lib/src/stream_completer.dart | 41 +++++----- pkgs/async/lib/src/stream_group.dart | 19 ++--- pkgs/async/lib/src/stream_queue.dart | 39 +++++---- pkgs/async/lib/src/stream_sink_completer.dart | 44 +++++----- .../lib/src/stream_sink_transformer.dart | 6 +- .../handler_transformer.dart | 27 +++--- .../stream_transformer_wrapper.dart | 2 +- pkgs/async/lib/src/stream_splitter.dart | 16 ++-- .../src/stream_subscription_transformer.dart | 30 +++---- pkgs/async/lib/src/stream_zip.dart | 14 ++-- pkgs/async/lib/src/subscription_stream.dart | 38 ++++----- .../lib/src/typed/stream_subscription.dart | 12 +-- pkgs/async/pubspec.yaml | 82 ++++++++++++++++++- pkgs/async/test/async_cache_test.dart | 14 ++-- pkgs/async/test/async_memoizer_test.dart | 2 +- .../async/test/cancelable_operation_test.dart | 22 ++--- pkgs/async/test/future_group_test.dart | 2 +- pkgs/async/test/lazy_stream_test.dart | 6 +- .../test/result/result_captureAll_test.dart | 2 +- .../test/result/result_flattenAll_test.dart | 4 +- .../async/test/result/result_future_test.dart | 8 +- pkgs/async/test/result/result_test.dart | 39 ++++----- pkgs/async/test/stream_completer_test.dart | 20 ++--- pkgs/async/test/stream_group_test.dart | 10 +-- pkgs/async/test/stream_queue_test.dart | 18 ++-- .../test/stream_sink_completer_test.dart | 48 +++++------ .../test/stream_sink_transformer_test.dart | 6 +- pkgs/async/test/stream_splitter_test.dart | 4 +- pkgs/async/test/stream_zip_test.dart | 4 +- pkgs/async/test/stream_zip_zone_test.dart | 2 +- pkgs/async/test/subscription_stream_test.dart | 9 +- .../test/subscription_transformer_test.dart | 7 +- .../stream_subscription_test.dart | 14 ++-- pkgs/async/test/utils.dart | 16 ++-- 53 files changed, 461 insertions(+), 361 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index 6fb2f8dd..0789302f 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -1,18 +1,33 @@ language: dart dart: - - dev - - 2.2.0 + - dev -dart_task: - - test: --platform vm - - test: --platform chrome - - dartanalyzer - - dartfmt +jobs: + include: + - stage: analyze_and_format + name: "Analyzer" + dart: be/raw/latest + os: linux + script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . + - stage: analyze_and_format + name: "Format" + dart: be/raw/latest + os: linux + script: dartfmt -n --set-exit-if-changed . + - stage: test + name: "Vm Tests" + dart: be/raw/latest + os: linux + script: pub run --enable-experiment=non-nullable test -p vm + +stages: + - analyze_and_format + - test # Only building master means that we don't run two builds for each pull request. branches: - only: [master] + only: [master, null_safety] cache: directories: diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index f247608b..3ca7f6c2 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.0-nullsafety + +* Migrate this package to null safety. + ## 2.4.2 * `StreamQueue` starts listening immediately to broadcast strings. diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 18c26e0a..b58ef994 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -7,6 +7,8 @@ analyzer: todo: ignore # Lint provided by pkg:pedantic – should fix this! unawaited_futures: ignore + enable-experiment: + - non-nullable linter: rules: diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index e0a6f498..d6f5f7f8 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -29,13 +29,13 @@ class AsyncCache { final Duration _duration; /// Cached results of a previous [fetchStream] call. - StreamSplitter _cachedStreamSplitter; + StreamSplitter? _cachedStreamSplitter; /// Cached results of a previous [fetch] call. - Future _cachedValueFuture; + Future? _cachedValueFuture; /// Fires when the cache should be considered stale. - Timer _stale; + Timer? _stale; /// Creates a cache that invalidates its contents after [duration] has passed. /// @@ -65,7 +65,7 @@ class AsyncCache { await _cachedValueFuture; _startStaleTimer(); } - return _cachedValueFuture; + return _cachedValueFuture!; } /// Returns a cached stream from a previous call to [fetchStream], or runs @@ -78,12 +78,12 @@ class AsyncCache { if (_cachedValueFuture != null) { throw StateError('Previously used to cache via `fetch`'); } - _cachedStreamSplitter ??= StreamSplitter( + var splitter = _cachedStreamSplitter ??= StreamSplitter( callback().transform(StreamTransformer.fromHandlers(handleDone: (sink) { _startStaleTimer(); sink.close(); }))); - return _cachedStreamSplitter.split(); + return splitter.split(); } /// Removes any cached value. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 9fd05347..267c6d16 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -35,7 +35,7 @@ class CancelableOperation { /// moment this [CancelableOperation] is created, regardless of whether /// [inner] has completed yet or not. factory CancelableOperation.fromFuture(Future inner, - {FutureOr Function() onCancel}) { + {FutureOr Function()? onCancel}) { var completer = CancelableCompleter(onCancel: onCancel); completer.complete(inner); return completer.operation; @@ -55,7 +55,7 @@ class CancelableOperation { value.then((value) { controller.add(value); controller.close(); - }, onError: (error, StackTrace stackTrace) { + }, onError: (Object error, StackTrace stackTrace) { controller.addError(error, stackTrace); controller.close(); }); @@ -68,8 +68,8 @@ class CancelableOperation { /// If this operation completes, this completes to the same result as [value]. /// If this operation is cancelled, the returned future waits for the future /// returned by [cancel], then completes to [cancellationValue]. - Future valueOrCancellation([T cancellationValue]) { - var completer = Completer.sync(); + Future valueOrCancellation([T? cancellationValue]) { + var completer = Completer.sync(); value.then((result) => completer.complete(result), onError: completer.completeError); @@ -93,23 +93,24 @@ class CancelableOperation { /// If [propagateCancel] is `true` and the returned operation is canceled then /// this operation is canceled. The default is `false`. CancelableOperation then(FutureOr Function(T) onValue, - {FutureOr Function(Object, StackTrace) onError, - FutureOr Function() onCancel, + {FutureOr Function(Object, StackTrace)? onError, + FutureOr Function()? onCancel, bool propagateCancel = false}) { final completer = CancelableCompleter(onCancel: propagateCancel ? cancel : null); - valueOrCancellation().then((T result) { + valueOrCancellation().then((T? result) { if (!completer.isCanceled) { if (isCompleted) { - completer.complete(Future.sync(() => onValue(result))); + assert(result is T); + completer.complete(Future.sync(() => onValue(result!))); } else if (onCancel != null) { completer.complete(Future.sync(onCancel)); } else { completer._cancel(); } } - }, onError: (error, StackTrace stackTrace) { + }, onError: (Object error, StackTrace stackTrace) { if (!completer.isCanceled) { if (onError != null) { completer.complete(Future.sync(() => onError(error, stackTrace))); @@ -145,7 +146,7 @@ class CancelableCompleter { final Completer _inner; /// The callback to call if the future is canceled. - final FutureOrCallback _onCancel; + final FutureOrCallback? _onCancel; /// Creates a new completer for a [CancelableOperation]. /// @@ -155,15 +156,14 @@ class CancelableCompleter { /// /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. - CancelableCompleter({FutureOr Function() onCancel}) + CancelableCompleter({FutureOr Function()? onCancel}) : _onCancel = onCancel, _inner = Completer() { - _operation = CancelableOperation._(this); + operation = CancelableOperation._(this); } /// The operation controlled by this completer. - CancelableOperation get operation => _operation; - CancelableOperation _operation; + late final CancelableOperation operation; /// Whether the completer has completed. bool get isCompleted => _isCompleted; @@ -180,7 +180,7 @@ class CancelableCompleter { /// /// If [value] is a [Future], this will complete to the result of that /// [Future] once it completes. - void complete([FutureOr value]) { + void complete([FutureOr? value]) { if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; @@ -200,14 +200,14 @@ class CancelableCompleter { future.then((result) { if (_isCanceled) return; _inner.complete(result); - }, onError: (error, StackTrace stackTrace) { + }, onError: (Object error, StackTrace stackTrace) { if (_isCanceled) return; _inner.completeError(error, stackTrace); }); } /// Completes [operation] to [error]. - void completeError(Object error, [StackTrace stackTrace]) { + void completeError(Object error, [StackTrace? stackTrace]) { if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; @@ -221,7 +221,8 @@ class CancelableCompleter { return _cancelMemo.runOnce(() { _isCanceled = true; - if (_onCancel != null) return _onCancel(); + var onCancel = _onCancel; + if (onCancel != null) return onCancel(); }); } } diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index bc33b193..33d88e98 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -33,7 +33,7 @@ class DelegatingEventSink implements EventSink { } @override - void addError(error, [StackTrace stackTrace]) { + void addError(error, [StackTrace? stackTrace]) { _sink.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 984caf6d..11554523 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -25,11 +25,11 @@ class DelegatingFuture implements Future { Stream asStream() => _future.asStream(); @override - Future catchError(Function onError, {bool Function(Object error) test}) => + Future catchError(Function onError, {bool Function(Object error)? test}) => _future.catchError(onError, test: test); @override - Future then(FutureOr Function(T) onValue, {Function onError}) => + Future then(FutureOr Function(T) onValue, {Function? onError}) => _future.then(onValue, onError: onError); @override @@ -37,6 +37,6 @@ class DelegatingFuture implements Future { _future.whenComplete(action); @override - Future timeout(Duration timeLimit, {FutureOr Function() onTimeout}) => + Future timeout(Duration timeLimit, {FutureOr Function()? onTimeout}) => _future.timeout(timeLimit, onTimeout: onTimeout); } diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index 9005d1d9..ad76e904 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -36,7 +36,7 @@ class DelegatingStreamSink implements StreamSink { } @override - void addError(error, [StackTrace stackTrace]) { + void addError(error, [StackTrace? stackTrace]) { _sink.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 392e27bd..45b11343 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -31,22 +31,22 @@ class DelegatingStreamSubscription implements StreamSubscription { : TypeSafeStreamSubscription(subscription); @override - void onData(void Function(T) handleData) { + void onData(void Function(T)? handleData) { _source.onData(handleData); } @override - void onError(Function handleError) { + void onError(Function? handleError) { _source.onError(handleError); } @override - void onDone(void Function() handleDone) { + void onDone(void Function()? handleDone) { _source.onDone(handleDone); } @override - void pause([Future resumeFuture]) { + void pause([Future? resumeFuture]) { _source.pause(resumeFuture); } @@ -59,7 +59,7 @@ class DelegatingStreamSubscription implements StreamSubscription { Future cancel() => _source.cancel(); @override - Future asFuture([E futureValue]) => _source.asFuture(futureValue); + Future asFuture([E? futureValue]) => _source.asFuture(futureValue); @override bool get isPaused => _source.isPaused; diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 402ae46a..3a6291fd 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -44,13 +44,13 @@ class FutureGroup implements Sink> { Stream get onIdle => (_onIdleController ??= StreamController.broadcast(sync: true)).stream; - StreamController _onIdleController; + StreamController? _onIdleController; /// The values emitted by the futures that have been added to the group, in /// the order they were added. /// /// The slots for futures that haven't completed yet are `null`. - final _values = []; + final _values = []; /// Wait for [task] to complete. @override @@ -71,12 +71,13 @@ class FutureGroup implements Sink> { _values[index] = value; if (_pending != 0) return null; - if (_onIdleController != null) _onIdleController.add(null); + var onIdleController = _onIdleController; + if (onIdleController != null) onIdleController.add(null); if (!_closed) return null; - if (_onIdleController != null) _onIdleController.close(); - _completer.complete(_values); - }).catchError((error, StackTrace stackTrace) { + if (onIdleController != null) onIdleController.close(); + _completer.complete(_values.whereType().toList()); + }).catchError((Object error, StackTrace stackTrace) { if (_completer.isCompleted) return null; _completer.completeError(error, stackTrace); }); @@ -89,6 +90,6 @@ class FutureGroup implements Sink> { _closed = true; if (_pending != 0) return; if (_completer.isCompleted) return; - _completer.complete(_values); + _completer.complete(_values.whereType().toList()); } } diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index f5e65d13..e6779ee5 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -15,7 +15,7 @@ import 'utils.dart'; /// produce a `Stream`. class LazyStream extends Stream { /// The callback that's called to create the inner stream. - FutureOrCallback> _callback; + FutureOrCallback>? _callback; /// Creates a single-subscription `Stream` that calls [callback] when it gets /// a listener and forwards to the returned stream. @@ -25,15 +25,15 @@ class LazyStream extends Stream { } @override - StreamSubscription listen(void Function(T) onData, - {Function onError, void Function() onDone, bool cancelOnError}) { - if (_callback == null) { + StreamSubscription listen(void Function(T)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { + var callback = _callback; + if (callback == null) { throw StateError('Stream has already been listened to.'); } // Null out the callback before we invoke it to ensure that even while // running it, this can't be called twice. - var callback = _callback; _callback = null; var result = callback(); @@ -41,7 +41,7 @@ class LazyStream extends Stream { if (result is Future>) { stream = StreamCompleter.fromFuture(result); } else { - stream = result as Stream; + stream = result; } return stream.listen(onData, diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart index b28df2a6..34b100bb 100644 --- a/pkgs/async/lib/src/null_stream_sink.dart +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -43,12 +43,12 @@ class NullStreamSink implements StreamSink { /// /// If [done] is passed, it's used as the [Sink.done] future. Otherwise, a /// completed future is used. - NullStreamSink({Future done}) : done = done ?? Future.value(); + NullStreamSink({Future? done}) : done = done ?? Future.value(); /// Creates a null sink whose [done] future emits [error]. /// /// Note that this error will not be considered uncaught. - NullStreamSink.error(error, [StackTrace stackTrace]) + NullStreamSink.error(Object error, [StackTrace? stackTrace]) : done = Future.error(error, stackTrace) // Don't top-level the error. This gives the user a change to call // [close] or [done], and matches the behavior of a remote endpoint @@ -61,7 +61,7 @@ class NullStreamSink implements StreamSink { } @override - void addError(error, [StackTrace stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { _checkEventAllowed(); } @@ -70,7 +70,7 @@ class NullStreamSink implements StreamSink { _checkEventAllowed(); _addingStream = true; - var future = stream.listen(null).cancel() ?? Future.value(); + var future = stream.listen(null).cancel(); return future.whenComplete(() { _addingStream = false; }); diff --git a/pkgs/async/lib/src/result/capture_sink.dart b/pkgs/async/lib/src/result/capture_sink.dart index a7c3a592..562f5f95 100644 --- a/pkgs/async/lib/src/result/capture_sink.dart +++ b/pkgs/async/lib/src/result/capture_sink.dart @@ -18,7 +18,7 @@ class CaptureSink implements EventSink { } @override - void addError(Object error, [StackTrace stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { _sink.add(Result.error(error, stackTrace)); } diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index 76e02751..27ce155d 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -8,7 +8,7 @@ import 'result.dart'; import 'value.dart'; /// A result representing a thrown error. -class ErrorResult implements Result { +class ErrorResult implements Result { /// The error object that was thrown. final Object error; @@ -20,11 +20,13 @@ class ErrorResult implements Result { @override bool get isError => true; @override - ValueResult get asValue => null; + ValueResult? get asValue => null; @override ErrorResult get asError => this; - ErrorResult(this.error, this.stackTrace); + ErrorResult(this.error, [StackTrace? stackTrace]) + // TODO: Use AsyncError.defaultStackTrace(error) once available + : stackTrace = stackTrace ?? StackTrace.fromString(''); @override void complete(Completer completer) { @@ -37,7 +39,7 @@ class ErrorResult implements Result { } @override - Future get asFuture => Future.error(error, stackTrace); + Future get asFuture => Future.error(error, stackTrace); /// Calls an error handler with the error and stacktrace. /// diff --git a/pkgs/async/lib/src/result/future.dart b/pkgs/async/lib/src/result/future.dart index ff305460..20a5ebfd 100644 --- a/pkgs/async/lib/src/result/future.dart +++ b/pkgs/async/lib/src/result/future.dart @@ -16,8 +16,8 @@ class ResultFuture extends DelegatingFuture { /// The result of the wrapped [Future], if it's completed. /// /// If it hasn't completed yet, this will be `null`. - Result get result => _result; - Result _result; + Result? get result => _result; + Result? _result; ResultFuture(Future future) : super(future) { Result.capture(future).then((result) { diff --git a/pkgs/async/lib/src/result/release_sink.dart b/pkgs/async/lib/src/result/release_sink.dart index 5d8267a1..bf6dd505 100644 --- a/pkgs/async/lib/src/result/release_sink.dart +++ b/pkgs/async/lib/src/result/release_sink.dart @@ -18,7 +18,7 @@ class ReleaseSink implements EventSink> { } @override - void addError(Object error, [StackTrace stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { // Errors may be added by intermediate processing, even if it is never // added by CaptureSink. _sink.addError(error, stackTrace); diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index e6047822..5f93b551 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -63,7 +63,7 @@ abstract class Result { factory Result(T Function() computation) { try { return ValueResult(computation()); - } catch (e, s) { + } on Object catch (e, s) { return ErrorResult(e, s); } } @@ -76,7 +76,7 @@ abstract class Result { /// Creates a `Result` holding an error. /// /// Alias for [ErrorResult.ErrorResult]. - factory Result.error(Object error, [StackTrace stackTrace]) => + factory Result.error(Object error, [StackTrace? stackTrace]) => ErrorResult(error, stackTrace); /// Captures the result of a future into a `Result` future. @@ -85,7 +85,7 @@ abstract class Result { /// Errors have been converted to an [ErrorResult] value. static Future> capture(Future future) { return future.then((value) => ValueResult(value), - onError: (error, StackTrace stackTrace) => + onError: (Object error, StackTrace stackTrace) => ErrorResult(error, stackTrace)); } @@ -97,9 +97,9 @@ abstract class Result { /// wrapped as a [Result.value]. /// The returned future will never have an error. static Future>> captureAll(Iterable> elements) { - var results = >[]; + var results = ?>[]; var pending = 0; - Completer>> completer; + late Completer>> completer; for (var element in elements) { if (element is Future) { var i = results.length; @@ -108,15 +108,15 @@ abstract class Result { Result.capture(element).then((result) { results[i] = result; if (--pending == 0) { - completer.complete(results); + completer.complete(List.from(results)); } }); } else { - results.add(Result.value(element as T)); + results.add(Result.value(element)); } } if (pending == 0) { - return Future>>.value(results); + return Future.value(List.from(results)); } completer = Completer>>(); return completer.future; @@ -172,8 +172,8 @@ abstract class Result { /// Otherwise both levels of results are value results, and a single /// result with the value is returned. static Result flatten(Result> result) { - if (result.isValue) return result.asValue.value; - return result.asError; + if (result.isValue) return result.asValue!.value; + return result.asError!; } /// Converts a sequence of results to a result of a list. @@ -184,9 +184,9 @@ abstract class Result { var values = []; for (var result in results) { if (result.isValue) { - values.add(result.asValue.value); + values.add(result.asValue!.value); } else { - return result.asError; + return result.asError!; } } return Result>.value(values); @@ -205,12 +205,12 @@ abstract class Result { /// If this is a value result, returns itself. /// /// Otherwise returns `null`. - ValueResult get asValue; + ValueResult? get asValue; /// If this is an error result, returns itself. /// /// Otherwise returns `null`. - ErrorResult get asError; + ErrorResult? get asError; /// Completes a completer with this result. void complete(Completer completer); diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart index 5c1a60fc..7cfc4742 100644 --- a/pkgs/async/lib/src/result/value.dart +++ b/pkgs/async/lib/src/result/value.dart @@ -19,7 +19,7 @@ class ValueResult implements Result { @override ValueResult get asValue => this; @override - ErrorResult get asError => null; + ErrorResult? get asError => null; ValueResult(this.value); diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index fe939fc1..2e056b2f 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -11,14 +11,14 @@ import 'dart:async'; /// listening to a stream as soon as it's bound. /// /// This also casts the source stream's events to type `T`. If the cast fails, -/// the result stream will emit a [CastError]. This behavior is deprecated, and +/// the result stream will emit a [TypeError]. This behavior is deprecated, and /// should not be relied upon. class SingleSubscriptionTransformer extends StreamTransformerBase { const SingleSubscriptionTransformer(); @override Stream bind(Stream stream) { - StreamSubscription subscription; + late StreamSubscription subscription; var controller = StreamController(sync: true, onCancel: () => subscription.cancel()); subscription = stream.listen((value) { @@ -26,7 +26,7 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { // type parameter and avoid this conversion. try { controller.add(value as T); - } on CastError catch (error, stackTrace) { + } on TypeError catch (error, stackTrace) { controller.addError(error, stackTrace); } }, onError: controller.addError, onDone: controller.close); diff --git a/pkgs/async/lib/src/stream_completer.dart b/pkgs/async/lib/src/stream_completer.dart index 5128d57f..27034c2f 100644 --- a/pkgs/async/lib/src/stream_completer.dart +++ b/pkgs/async/lib/src/stream_completer.dart @@ -97,7 +97,7 @@ class StreamCompleter { /// /// Any one of [setSourceStream], [setEmpty], and [setError] may be called at /// most once. Trying to call any of them again will fail. - void setError(error, [StackTrace stackTrace]) { + void setError(Object error, [StackTrace? stackTrace]) { setSourceStream(Stream.fromFuture(Future.error(error, stackTrace))); } } @@ -108,30 +108,31 @@ class _CompleterStream extends Stream { /// /// Created if the user listens on this stream before the source stream /// is set, or if using [_setEmpty] so there is no source stream. - StreamController _controller; + StreamController? _controller; /// Source stream for the events provided by this stream. /// /// Set when the completer sets the source stream using [_setSourceStream] /// or [_setEmpty]. - Stream _sourceStream; + Stream? _sourceStream; @override - StreamSubscription listen(void Function(T) onData, - {Function onError, void Function() onDone, bool cancelOnError}) { + StreamSubscription listen(void Function(T)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { if (_controller == null) { - if (_sourceStream != null && !_sourceStream.isBroadcast) { + var sourceStream = _sourceStream; + if (sourceStream != null && !sourceStream.isBroadcast) { // If the source stream is itself single subscription, // just listen to it directly instead of creating a controller. - return _sourceStream.listen(onData, + return sourceStream.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } - _createController(); + _ensureController(); if (_sourceStream != null) { _linkStreamToController(); } } - return _controller.stream.listen(onData, + return _controller!.stream.listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } @@ -157,11 +158,10 @@ class _CompleterStream extends Stream { /// Links source stream to controller when both are available. void _linkStreamToController() { - assert(_controller != null); - assert(_sourceStream != null); - _controller - .addStream(_sourceStream, cancelOnError: false) - .whenComplete(_controller.close); + var controller = _controller!; + controller + .addStream(_sourceStream!, cancelOnError: false) + .whenComplete(controller.close); } /// Sets an empty source stream. @@ -170,16 +170,13 @@ class _CompleterStream extends Stream { /// immediately. void _setEmpty() { assert(_sourceStream == null); - if (_controller == null) { - _createController(); - } - _sourceStream = _controller.stream; // Mark stream as set. - _controller.close(); + var controller = _ensureController(); + _sourceStream = controller.stream; // Mark stream as set. + controller.close(); } // Creates the [_controller]. - void _createController() { - assert(_controller == null); - _controller = StreamController(sync: true); + StreamController _ensureController() { + return _controller ??= StreamController(sync: true); } } diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index e9a4afc3..8c17ec20 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -29,7 +29,7 @@ import 'dart:async'; class StreamGroup implements Sink> { /// The stream through which all events from streams in the group are emitted. Stream get stream => _controller.stream; - StreamController _controller; + late StreamController _controller; /// Whether the group is closed, meaning that no more streams may be added. var _closed = false; @@ -47,7 +47,7 @@ class StreamGroup implements Sink> { /// subscriptions will be canceled and set to null again. Single-subscriber /// stream subscriptions will be left intact, since they can't be /// re-subscribed. - final _subscriptions = , StreamSubscription>{}; + final _subscriptions = , StreamSubscription?>{}; /// Merges the events from [streams] into a single single-subscription stream. /// @@ -100,7 +100,7 @@ class StreamGroup implements Sink> { /// /// Throws a [StateError] if this group is closed. @override - Future add(Stream stream) { + Future? add(Stream stream) { if (_closed) { throw StateError("Can't add a Stream to a closed StreamGroup."); } @@ -130,7 +130,7 @@ class StreamGroup implements Sink> { /// /// If [stream]'s subscription is canceled, this returns /// [StreamSubscription.cancel]'s return value. Otherwise, it returns `null`. - Future remove(Stream stream) { + Future? remove(Stream stream) { var subscription = _subscriptions.remove(stream); var future = subscription == null ? null : subscription.cancel(); if (_closed && _subscriptions.isEmpty) _controller.close(); @@ -155,7 +155,7 @@ class StreamGroup implements Sink> { void _onPause() { _state = _StreamGroupState.paused; for (var subscription in _subscriptions.values) { - subscription.pause(); + subscription!.pause(); } } @@ -163,19 +163,18 @@ class StreamGroup implements Sink> { void _onResume() { _state = _StreamGroupState.listening; for (var subscription in _subscriptions.values) { - subscription.resume(); + subscription!.resume(); } } /// A callback called when [stream] is canceled. /// /// This is only called for single-subscription groups. - Future _onCancel() { + Future? _onCancel() { _state = _StreamGroupState.canceled; var futures = _subscriptions.values - .map((subscription) => subscription.cancel()) - .where((future) => future != null) + .map((subscription) => subscription!.cancel()) .toList(); _subscriptions.clear(); @@ -194,7 +193,7 @@ class StreamGroup implements Sink> { // will still be added to [_controller], but then they'll be dropped since // it has no listeners. if (!stream.isBroadcast) return; - subscription.cancel(); + subscription!.cancel(); _subscriptions[stream] = null; }); } diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 278e249a..5aa60548 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -87,7 +87,7 @@ class StreamQueue { /// /// Set to subscription when listening, and set to `null` when the /// subscription is done (and [_isDone] is set to true). - StreamSubscription _subscription; + StreamSubscription? _subscription; /// Whether the event source is done. bool _isDone = false; @@ -343,7 +343,7 @@ class StreamQueue { transaction.reject(); } return result; - }, onError: (error) { + }, onError: (Object error) { transaction.commit(queue); throw error; }); @@ -399,7 +399,7 @@ class StreamQueue { /// After calling `cancel`, no further events can be requested. /// None of [lookAhead], [next], [peek], [rest], [skip], [take] or [cancel] /// may be called again. - Future cancel({bool immediate = false}) { + Future? cancel({bool immediate = false}) { if (_isClosed) throw _failClosed(); _isClosed = true; @@ -454,11 +454,10 @@ class StreamQueue { } _isDone = true; - if (_subscription == null) { + var subscription = _subscription; + if (subscription == null) { return _source; } - - var subscription = _subscription; _subscription = null; var wasPaused = subscription.isPaused; @@ -475,7 +474,7 @@ class StreamQueue { /// /// The event source is restarted by the next call to [_ensureListening]. void _pause() { - _subscription.pause(); + _subscription!.pause(); } /// Ensures that we are listening on events from the event source. @@ -488,22 +487,22 @@ class StreamQueue { if (_subscription == null) { _subscription = _source.listen((data) { _addResult(Result.value(data)); - }, onError: (error, StackTrace stackTrace) { + }, onError: (Object error, StackTrace stackTrace) { _addResult(Result.error(error, stackTrace)); }, onDone: () { _subscription = null; _close(); }); } else { - _subscription.resume(); + _subscription!.resume(); } } /// Cancels the underlying event source. - Future _cancel() { + Future? _cancel() { if (_isDone) return null; _subscription ??= _source.listen(null); - var future = _subscription.cancel(); + var future = _subscription!.cancel(); _close(); return future; } @@ -771,7 +770,8 @@ class _SkipRequest implements _EventRequest { var event = events.removeFirst(); if (event.isError) { - _completer.completeError(event.asError.error, event.asError.stackTrace); + _completer.completeError( + event.asError!.error, event.asError!.stackTrace); return true; } } @@ -814,10 +814,10 @@ class _TakeRequest extends _ListRequest { var event = events.removeFirst(); if (event.isError) { - event.asError.complete(_completer); + event.asError!.complete(_completer); return true; } - _list.add(event.asValue.value); + _list.add(event.asValue!.value); } _completer.complete(_list); return true; @@ -837,10 +837,10 @@ class _LookAheadRequest extends _ListRequest { } var event = events.elementAt(_list.length); if (event.isError) { - event.asError.complete(_completer); + event.asError!.complete(_completer); return true; } - _list.add(event.asValue.value); + _list.add(event.asValue!.value); } _completer.complete(_list); return true; @@ -954,8 +954,7 @@ class _HasNextRequest implements _EventRequest { /// [StreamQueue._updateRequests]. class _TransactionRequest implements _EventRequest { /// The transaction created by this request. - StreamQueueTransaction get transaction => _transaction; - StreamQueueTransaction _transaction; + late final StreamQueueTransaction transaction; /// The controller that passes events to [transaction]. final _controller = StreamController(sync: true); @@ -964,7 +963,7 @@ class _TransactionRequest implements _EventRequest { var _eventsSent = 0; _TransactionRequest(StreamQueue parent) { - _transaction = StreamQueueTransaction._(parent, _controller.stream); + transaction = StreamQueueTransaction._(parent, _controller.stream); } @override @@ -973,6 +972,6 @@ class _TransactionRequest implements _EventRequest { events[_eventsSent++].addTo(_controller); } if (isDone && !_controller.isClosed) _controller.close(); - return transaction._committed || _transaction._rejected; + return transaction._committed || transaction._rejected; } } diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index ebfd717d..10e549d8 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -70,7 +70,7 @@ class StreamSinkCompleter { /// /// Either of [setDestinationSink] or [setError] may be called at most once. /// Trying to call either of them again will fail. - void setError(error, [StackTrace stackTrace]) { + void setError(Object error, [StackTrace? stackTrace]) { setDestinationSink(NullStreamSink.error(error, stackTrace)); } } @@ -81,18 +81,18 @@ class _CompleterSink implements StreamSink { /// /// Created if the user adds events to this sink before the destination sink /// is set. - StreamController _controller; + StreamController? _controller; /// Completer for [done]. /// /// Created if the user requests the [done] future before the destination sink /// is set. - Completer _doneCompleter; + Completer? _doneCompleter; /// Destination sink for the events added to this sink. /// /// Set when [StreamSinkCompleter.setDestinationSink] is called. - StreamSink _destinationSink; + StreamSink? _destinationSink; /// Whether events should be sent directly to [_destinationSink], as opposed /// to going through [_controller]. @@ -100,56 +100,52 @@ class _CompleterSink implements StreamSink { @override Future get done { - if (_doneCompleter != null) return _doneCompleter.future; + if (_doneCompleter != null) return _doneCompleter!.future; if (_destinationSink == null) { _doneCompleter = Completer.sync(); - return _doneCompleter.future; + return _doneCompleter!.future; } - return _destinationSink.done; + return _destinationSink!.done; } @override void add(T event) { if (_canSendDirectly) { - _destinationSink.add(event); + _destinationSink!.add(event); } else { - _ensureController(); - _controller.add(event); + _ensureController().add(event); } } @override - void addError(error, [StackTrace stackTrace]) { + void addError(error, [StackTrace? stackTrace]) { if (_canSendDirectly) { - _destinationSink.addError(error, stackTrace); + _destinationSink!.addError(error, stackTrace); } else { - _ensureController(); - _controller.addError(error, stackTrace); + _ensureController().addError(error, stackTrace); } } @override Future addStream(Stream stream) { - if (_canSendDirectly) return _destinationSink.addStream(stream); + if (_canSendDirectly) return _destinationSink!.addStream(stream); - _ensureController(); - return _controller.addStream(stream, cancelOnError: false); + return _ensureController().addStream(stream, cancelOnError: false); } @override Future close() { if (_canSendDirectly) { - _destinationSink.close(); + _destinationSink!.close(); } else { - _ensureController(); - _controller.close(); + _ensureController().close(); } return done; } /// Create [_controller] if it doesn't yet exist. - void _ensureController() { - _controller ??= StreamController(sync: true); + StreamController _ensureController() { + return _controller ??= StreamController(sync: true); } /// Sets the destination sink to which events from this sink will be provided. @@ -168,7 +164,7 @@ class _CompleterSink implements StreamSink { // Catch any error that may come from [addStream] or [sink.close]. They'll // be reported through [done] anyway. sink - .addStream(_controller.stream) + .addStream(_controller!.stream) .whenComplete(sink.close) .catchError((_) {}); } @@ -176,7 +172,7 @@ class _CompleterSink implements StreamSink { // If the user has already asked when the sink is done, connect the sink's // done callback to that completer. if (_doneCompleter != null) { - _doneCompleter.complete(sink.done); + _doneCompleter!.complete(sink.done); } } } diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index 1dc27ed0..bdcb1966 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -34,9 +34,9 @@ abstract class StreamSinkTransformer { /// they're passed are forwarded to the inner sink. If a handler is omitted, /// the event is passed through unaltered. factory StreamSinkTransformer.fromHandlers( - {void Function(S, EventSink) handleData, - void Function(Object, StackTrace, EventSink) handleError, - void Function(EventSink) handleDone}) { + {void Function(S, EventSink)? handleData, + void Function(Object, StackTrace, EventSink)? handleError, + void Function(EventSink)? handleDone}) { return HandlerTransformer(handleData, handleError, handleDone); } diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index 8d37ce3f..b112e3ec 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -11,6 +11,9 @@ import '../delegate/stream_sink.dart'; typedef HandleData = void Function(S data, EventSink sink); /// The type of the callback for handling error events. +// +// TODO: Update to take a non-nullable StackTrace once that change lands in +// the sdk. typedef HandleError = void Function( Object error, StackTrace stackTrace, EventSink sink); @@ -20,13 +23,13 @@ typedef HandleDone = void Function(EventSink sink); /// A [StreamSinkTransformer] that delegates events to the given handlers. class HandlerTransformer implements StreamSinkTransformer { /// The handler for data events. - final HandleData _handleData; + final HandleData? _handleData; /// The handler for error events. - final HandleError _handleError; + final HandleError? _handleError; /// The handler for done events. - final HandleDone _handleDone; + final HandleDone? _handleDone; HandlerTransformer(this._handleData, this._handleError, this._handleDone); @@ -55,19 +58,22 @@ class _HandlerSink implements StreamSink { @override void add(S event) { - if (_transformer._handleData == null) { + var handleData = _transformer._handleData; + if (handleData == null) { _inner.add(event as T); } else { - _transformer._handleData(event, _safeCloseInner); + handleData(event, _safeCloseInner); } } @override - void addError(error, [StackTrace stackTrace]) { - if (_transformer._handleError == null) { + void addError(error, [StackTrace? stackTrace]) { + var handleError = _transformer._handleError; + if (handleError == null) { _inner.addError(error, stackTrace); } else { - _transformer._handleError(error, stackTrace, _safeCloseInner); + handleError(error, stackTrace ?? AsyncError.defaultStackTrace(error), + _safeCloseInner); } } @@ -82,9 +88,10 @@ class _HandlerSink implements StreamSink { @override Future close() { - if (_transformer._handleDone == null) return _inner.close(); + var handleDone = _transformer._handleDone; + if (handleDone == null) return _inner.close(); - _transformer._handleDone(_safeCloseInner); + handleDone(_safeCloseInner); return _inner.done; } } diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart index 1df7e5af..2afcbff8 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -50,7 +50,7 @@ class _StreamTransformerWrapperSink implements StreamSink { } @override - void addError(error, [StackTrace stackTrace]) { + void addError(error, [StackTrace? stackTrace]) { _controller.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/stream_splitter.dart b/pkgs/async/lib/src/stream_splitter.dart index e9f326e2..f7377d67 100644 --- a/pkgs/async/lib/src/stream_splitter.dart +++ b/pkgs/async/lib/src/stream_splitter.dart @@ -28,7 +28,7 @@ class StreamSplitter { /// The subscription to [_stream]. /// /// This will be `null` until a branch has a listener. - StreamSubscription _subscription; + StreamSubscription? _subscription; /// The buffer of events or errors that have already been emitted by /// [_stream]. @@ -57,7 +57,7 @@ class StreamSplitter { /// /// [count] defaults to 2. This is the same as creating [count] branches and /// then closing the [StreamSplitter]. - static List> splitFrom(Stream stream, [int count]) { + static List> splitFrom(Stream stream, [int? count]) { count ??= 2; var splitter = StreamSplitter(stream); var streams = List>.generate(count, (_) => splitter.split()); @@ -125,8 +125,8 @@ class StreamSplitter { assert(_controllers.isEmpty); assert(_isClosed); - Future future; - if (_subscription != null) future = _subscription.cancel(); + Future? future; + if (_subscription != null) future = _subscription!.cancel(); if (future != null) _closeGroup.add(future); _closeGroup.close(); } @@ -142,7 +142,7 @@ class StreamSplitter { // Resume the subscription in case it was paused, either because all the // controllers were paused or because the last one was canceled. If it // wasn't paused, this will be a no-op. - _subscription.resume(); + _subscription!.resume(); } else { _subscription = _stream.listen(_onData, onError: _onError, onDone: _onDone); @@ -152,14 +152,14 @@ class StreamSplitter { /// Pauses [_subscription] if every controller is paused. void _onPause() { if (!_controllers.every((controller) => controller.isPaused)) return; - _subscription.pause(); + _subscription!.pause(); } /// Resumes [_subscription]. /// /// If [_subscription] wasn't paused, this is a no-op. void _onResume() { - _subscription.resume(); + _subscription!.resume(); } /// Removes [controller] from [_controllers] and cancels or pauses @@ -175,7 +175,7 @@ class StreamSplitter { if (_isClosed) { _cancelSubscription(); } else { - _subscription.pause(); + _subscription!.pause(); } } diff --git a/pkgs/async/lib/src/stream_subscription_transformer.dart b/pkgs/async/lib/src/stream_subscription_transformer.dart index 0e289722..d03ea700 100644 --- a/pkgs/async/lib/src/stream_subscription_transformer.dart +++ b/pkgs/async/lib/src/stream_subscription_transformer.dart @@ -28,9 +28,9 @@ typedef _VoidHandler = void Function(StreamSubscription inner); /// [StreamSubscription]: [handleCancel] must call `cancel()`, [handlePause] /// must call `pause()`, and [handleResume] must call `resume()`. StreamTransformer subscriptionTransformer( - {Future Function(StreamSubscription) handleCancel, - void Function(StreamSubscription) handlePause, - void Function(StreamSubscription) handleResume}) { + {Future Function(StreamSubscription)? handleCancel, + void Function(StreamSubscription)? handlePause, + void Function(StreamSubscription)? handleResume}) { return StreamTransformer((stream, cancelOnError) { return _TransformedSubscription( stream.listen(null, cancelOnError: cancelOnError), @@ -50,7 +50,7 @@ StreamTransformer subscriptionTransformer( /// methods. class _TransformedSubscription implements StreamSubscription { /// The wrapped subscription. - StreamSubscription _inner; + StreamSubscription? _inner; /// The callback to run when [cancel] is called. final _AsyncHandler _handleCancel; @@ -68,47 +68,47 @@ class _TransformedSubscription implements StreamSubscription { this._inner, this._handleCancel, this._handlePause, this._handleResume); @override - void onData(void Function(T) handleData) { + void onData(void Function(T)? handleData) { _inner?.onData(handleData); } @override - void onError(Function handleError) { + void onError(Function? handleError) { _inner?.onError(handleError); } @override - void onDone(void Function() handleDone) { + void onDone(void Function()? handleDone) { _inner?.onDone(handleDone); } @override Future cancel() => _cancelMemoizer.runOnce(() { - var inner = _inner; - _inner.onData(null); - _inner.onDone(null); + var inner = _inner!; + inner.onData(null); + inner.onDone(null); // Setting onError to null will cause errors to be top-leveled. - _inner.onError((_, __) {}); + inner.onError((_, __) {}); _inner = null; return _handleCancel(inner); }); final _cancelMemoizer = AsyncMemoizer(); @override - void pause([Future resumeFuture]) { + void pause([Future? resumeFuture]) { if (_cancelMemoizer.hasRun) return; if (resumeFuture != null) resumeFuture.whenComplete(resume); - _handlePause(_inner); + _handlePause(_inner!); } @override void resume() { if (_cancelMemoizer.hasRun) return; - _handleResume(_inner); + _handleResume(_inner!); } @override - Future asFuture([E futureValue]) => + Future asFuture([E? futureValue]) => _inner?.asFuture(futureValue) ?? Completer().future; } diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index e319746a..506bf6d9 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -18,12 +18,12 @@ class StreamZip extends Stream> { StreamZip(Iterable> streams) : _streams = streams; @override - StreamSubscription> listen(void Function(List) onData, - {Function onError, void Function() onDone, bool cancelOnError}) { + StreamSubscription> listen(void Function(List)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { cancelOnError = identical(true, cancelOnError); var subscriptions = >[]; - StreamController> controller; - List current; + late StreamController> controller; + late List current; var dataCount = 0; /// Called for each data from a subscription in [subscriptions]. @@ -31,8 +31,8 @@ class StreamZip extends Stream> { current[index] = data; dataCount++; if (dataCount == subscriptions.length) { - var data = current; - current = List(subscriptions.length); + var data = List.from(current); + current = List.filled(subscriptions.length, null); dataCount = 0; for (var i = 0; i < subscriptions.length; i++) { if (i != index) subscriptions[i].resume(); @@ -85,7 +85,7 @@ class StreamZip extends Stream> { rethrow; } - current = List(subscriptions.length); + current = List.filled(subscriptions.length, null); controller = StreamController>(onPause: () { for (var i = 0; i < subscriptions.length; i++) { diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index b4286193..2c94e05d 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -18,7 +18,7 @@ import 'delegate/stream_subscription.dart'; /// If other code is accessing the subscription, results may be unpredictable. class SubscriptionStream extends Stream { /// The subscription providing the events for this stream. - StreamSubscription _source; + StreamSubscription? _source; /// Create a single-subscription `Stream` from [subscription]. /// @@ -31,21 +31,22 @@ class SubscriptionStream extends Stream { /// an error. SubscriptionStream(StreamSubscription subscription) : _source = subscription { - _source.pause(); + var source = _source!; + source.pause(); // Clear callbacks to avoid keeping them alive unnecessarily. - _source.onData(null); - _source.onError(null); - _source.onDone(null); + source.onData(null); + source.onError(null); + source.onDone(null); } @override - StreamSubscription listen(void Function(T) onData, - {Function onError, void Function() onDone, bool cancelOnError}) { - if (_source == null) { + StreamSubscription listen(void Function(T)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { + var subscription = _source; + if (subscription == null) { throw StateError('Stream has already been listened to.'); } cancelOnError = (true == cancelOnError); - var subscription = _source; _source = null; var result = cancelOnError @@ -71,26 +72,17 @@ class _CancelOnErrorSubscriptionWrapper : super(subscription); @override - void onError(Function handleError) { + void onError(Function? handleError) { // Cancel when receiving an error. super.onError((error, StackTrace stackTrace) { - var cancelFuture = super.cancel(); - if (cancelFuture != null) { - // Wait for the cancel to complete before sending the error event. - cancelFuture.whenComplete(() { - if (handleError is ZoneBinaryCallback) { - handleError(error, stackTrace); - } else { - handleError(error); - } - }); - } else { + // Wait for the cancel to complete before sending the error event. + super.cancel().whenComplete(() { if (handleError is ZoneBinaryCallback) { handleError(error, stackTrace); - } else { + } else if (handleError != null) { handleError(error); } - } + }); }); } } diff --git a/pkgs/async/lib/src/typed/stream_subscription.dart b/pkgs/async/lib/src/typed/stream_subscription.dart index d85b7760..fe91656c 100644 --- a/pkgs/async/lib/src/typed/stream_subscription.dart +++ b/pkgs/async/lib/src/typed/stream_subscription.dart @@ -13,22 +13,23 @@ class TypeSafeStreamSubscription implements StreamSubscription { TypeSafeStreamSubscription(this._subscription); @override - void onData(void Function(T) handleData) { + void onData(void Function(T)? handleData) { + if (handleData == null) return _subscription.onData(null); _subscription.onData((data) => handleData(data as T)); } @override - void onError(Function handleError) { + void onError(Function? handleError) { _subscription.onError(handleError); } @override - void onDone(void Function() handleDone) { + void onDone(void Function()? handleDone) { _subscription.onDone(handleDone); } @override - void pause([Future resumeFuture]) { + void pause([Future? resumeFuture]) { _subscription.pause(resumeFuture); } @@ -41,5 +42,6 @@ class TypeSafeStreamSubscription implements StreamSubscription { Future cancel() => _subscription.cancel(); @override - Future asFuture([E futureValue]) => _subscription.asFuture(futureValue); + Future asFuture([E? futureValue]) => + _subscription.asFuture(futureValue); } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0cb71cf9..bfdf20e1 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,11 +1,11 @@ name: async -version: 2.4.2 +version: 2.5.0-nullsafety description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async environment: - sdk: '>=2.2.0 <3.0.0' + sdk: '>=2.9.0-20.0.dev <2.9.0' dependencies: collection: ^1.5.0 @@ -15,3 +15,81 @@ dev_dependencies: stack_trace: ^1.0.0 test: ^1.0.0 pedantic: ^1.0.0 + +dependency_overrides: + boolean_selector: + git: + url: git://github.com/dart-lang/boolean_selector.git + ref: null_safety + charcode: + git: + url: git://github.com/dart-lang/charcode.git + ref: null_safety + clock: + git: + url: git://github.com/dart-lang/clock.git + ref: null_safety + collection: + git: + url: git://github.com/dart-lang/collection.git + ref: null_safety + fake_async: + git: + url: git://github.com/dart-lang/fake_async.git + ref: null_safety + matcher: + git: + url: git://github.com/dart-lang/matcher.git + ref: null_safety + meta: + git: + url: git://github.com/dart-lang/sdk.git + ref: null_safety-pkgs + path: pkg/meta + path: + git: + url: git://github.com/dart-lang/path.git + ref: null_safety + pedantic: + git: + url: git://github.com/dart-lang/pedantic.git + ref: null_safety + pool: + git: + url: git://github.com/dart-lang/pool.git + ref: null_safety + source_span: + git: + url: git://github.com/dart-lang/source_span.git + ref: null_safety + stack_trace: + git: + url: git://github.com/dart-lang/stack_trace.git + ref: null_safety + stream_channel: + git: + url: git://github.com/dart-lang/stream_channel.git + ref: null_safety + string_scanner: + git: + url: git://github.com/dart-lang/string_scanner.git + ref: null_safety + term_glyph: + git: + url: git://github.com/dart-lang/term_glyph.git + ref: null_safety + test_api: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test_api + test_core: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test_core + test: + git: + url: git://github.com/dart-lang/test.git + ref: null_safety + path: pkgs/test diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 4efbb69f..81e43e17 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -9,7 +9,7 @@ import 'package:fake_async/fake_async.dart'; import 'package:test/test.dart'; void main() { - AsyncCache cache; + late AsyncCache cache; setUp(() { // Create a cache that is fresh for an hour. @@ -22,7 +22,8 @@ void main() { test('should not fetch via callback when a cache exists', () async { await cache.fetch(() async => 'Expensive'); - expect(await cache.fetch(expectAsync0(() => null, count: 0)), 'Expensive'); + expect(await cache.fetch(expectAsync0(() async => 'fake', count: 0)), + 'Expensive'); }); test('should not fetch via callback when a future is in-flight', () async { @@ -31,7 +32,7 @@ void main() { var completer = Completer(); expect(cache.fetch(() => completer.future), completion('Expensive')); - expect(cache.fetch(expectAsync0(() => null, count: 0)), + expect(cache.fetch(expectAsync0(() async => 'fake', count: 0)), completion('Expensive')); completer.complete('Expensive'); }); @@ -79,7 +80,10 @@ void main() { yield '2'; yield '3'; }).toList(); - expect(await cache.fetchStream(expectAsync0(() => null, count: 0)).toList(), + expect( + await cache + .fetchStream(expectAsync0(() => Stream.empty(), count: 0)) + .toList(), ['1', '2', '3']); }); @@ -148,7 +152,7 @@ void main() { test('should pause a cached stream without affecting others', () async { Stream call() => Stream.fromIterable(['1', '2', '3']); - StreamSubscription sub; + late StreamSubscription sub; sub = cache.fetchStream(call).listen(expectAsync1((event) { if (event == '1') sub.pause(); })); diff --git a/pkgs/async/test/async_memoizer_test.dart b/pkgs/async/test/async_memoizer_test.dart index 982f7c9b..490b389d 100644 --- a/pkgs/async/test/async_memoizer_test.dart +++ b/pkgs/async/test/async_memoizer_test.dart @@ -6,7 +6,7 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; void main() { - AsyncMemoizer cache; + late AsyncMemoizer cache; setUp(() => cache = AsyncMemoizer()); test('runs the function only the first time runOnce() is called', () async { diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index b38c0b93..c87e43a8 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -11,7 +11,7 @@ import 'utils.dart'; void main() { group('without being canceled', () { - CancelableCompleter completer; + late CancelableCompleter completer; setUp(() { completer = CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)); }); @@ -116,7 +116,7 @@ void main() { test('fires onCancel', () { var canceled = false; - CancelableCompleter completer; + late CancelableCompleter completer; completer = CancelableCompleter(onCancel: expectAsync0(() { expect(completer.isCanceled, isTrue); canceled = true; @@ -243,23 +243,23 @@ void main() { }); group('then', () { - FutureOr Function(int) onValue; - FutureOr Function(Object, StackTrace) onError; - FutureOr Function() onCancel; - bool propagateCancel; - CancelableCompleter originalCompleter; + FutureOr Function(int)? onValue; + FutureOr Function(Object, StackTrace)? onError; + FutureOr Function()? onCancel; + late bool propagateCancel; + late CancelableCompleter originalCompleter; setUp(() { // Initialize all functions to ones that expect to not be called. - onValue = expectAsync1((_) => null, count: 0, id: 'onValue'); - onError = expectAsync2((e, s) => null, count: 0, id: 'onError'); - onCancel = expectAsync0(() => null, count: 0, id: 'onCancel'); + onValue = expectAsync1((_) => 'Fake', count: 0, id: 'onValue'); + onError = expectAsync2((e, s) => 'Fake', count: 0, id: 'onError'); + onCancel = expectAsync0(() => 'Fake', count: 0, id: 'onCancel'); propagateCancel = false; }); CancelableOperation runThen() { originalCompleter = CancelableCompleter(); - return originalCompleter.operation.then(onValue, + return originalCompleter.operation.then(onValue!, onError: onError, onCancel: onCancel, propagateCancel: propagateCancel); diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index f99d06df..22e90f89 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - FutureGroup futureGroup; + late FutureGroup futureGroup; setUp(() { futureGroup = FutureGroup(); }); diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index 2affe5ba..32c6b142 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -10,10 +10,6 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - test('disallows a null callback', () { - expect(() => LazyStream(null), throwsArgumentError); - }); - test('calls the callback when the stream is listened', () async { var callbackCalled = false; var stream = LazyStream(expectAsync0(() { @@ -96,7 +92,7 @@ void main() { }); test("a lazy stream can't be listened to from within its callback", () { - LazyStream stream; + late LazyStream stream; stream = LazyStream(expectAsync0(() { expect(() => stream.listen(null), throwsStateError); return Stream.empty(); diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index 8e798720..b992e1f0 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -14,7 +14,7 @@ Result err(n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of futures. Iterable> futures(int count, - {bool Function(int index) throwWhen}) sync* { + {bool Function(int index)? throwWhen}) sync* { for (var i = 0; i < count; i++) { if (throwWhen != null && throwWhen(i)) { yield Future.error('$i', someStack); diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart index c0a86035..b87fec46 100644 --- a/pkgs/async/test/result/result_flattenAll_test.dart +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -11,7 +11,7 @@ Result err(n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of results. Iterable> results(int count, - {bool Function(int index) throwWhen}) sync* { + {bool Function(int index)? throwWhen}) sync* { for (var i = 0; i < count; i++) { if (throwWhen != null && throwWhen(i)) { yield err(i); @@ -27,7 +27,7 @@ void main() { expect(result, expectation); } else { expect(result.isValue, true); - expect(result.asValue.value, expectation.asValue.value); + expect(result.asValue!.value, expectation.asValue!.value); } } diff --git a/pkgs/async/test/result/result_future_test.dart b/pkgs/async/test/result/result_future_test.dart index 4218db2b..71710054 100644 --- a/pkgs/async/test/result/result_future_test.dart +++ b/pkgs/async/test/result/result_future_test.dart @@ -9,8 +9,8 @@ import 'package:stack_trace/stack_trace.dart'; import 'package:test/test.dart'; void main() { - Completer completer; - ResultFuture future; + late Completer completer; + late ResultFuture future; setUp(() { completer = Completer(); future = ResultFuture(completer.future); @@ -25,7 +25,7 @@ void main() { // The completer calls its listeners asynchronously. We have to wait // before we can access the result. - expect(future.then((_) => future.result.asValue.value), + expect(future.then((_) => future.result!.asValue!.value), completion(equals(12))); }); @@ -36,7 +36,7 @@ void main() { // The completer calls its listeners asynchronously. We have to wait // before we can access the result. return future.catchError((_) {}).then((_) { - var error = future.result.asError; + var error = future.result!.asError!; expect(error.error, equals('error')); expect(error.stackTrace, equals(trace)); }); diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index adbc4457..154785ba 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -16,7 +16,7 @@ void main() { var result = Result.value(42); expect(result.isValue, isTrue); expect(result.isError, isFalse); - ValueResult value = result.asValue; + ValueResult value = result.asValue!; expect(value.value, equals(42)); }); @@ -24,7 +24,7 @@ void main() { Result result = ValueResult(42); expect(result.isValue, isTrue); expect(result.isError, isFalse); - var value = result.asValue; + var value = result.asValue!; expect(value.value, equals(42)); }); @@ -32,7 +32,7 @@ void main() { var result = Result.error('BAD', stack); expect(result.isValue, isFalse); expect(result.isError, isTrue); - var error = result.asError; + var error = result.asError!; expect(error.error, equals('BAD')); expect(error.stackTrace, same(stack)); }); @@ -50,9 +50,10 @@ void main() { var result = Result.error('BAD'); expect(result.isValue, isFalse); expect(result.isError, isTrue); - var error = result.asError; + var error = result.asError!; expect(error.error, equals('BAD')); - expect(error.stackTrace, isNull); + // A default stack trace is created + expect(error.stackTrace, isNotNull); }); test('complete with value', () { @@ -71,7 +72,7 @@ void main() { var c = Completer(); c.future.then((bool v) { fail('Unexpected value $v'); - }, onError: expectAsync2((e, s) { + }).then((_) {}, onError: expectAsync2((e, s) { expect(e, equals('BAD')); expect(s, same(stack)); })); @@ -108,7 +109,7 @@ void main() { Result result = ErrorResult('BAD', stack); result.asFuture.then((bool v) { fail('Unexpected value $v'); - }, onError: expectAsync2((e, s) { + }).then((_) {}, onError: expectAsync2((e, s) { expect(e, equals('BAD')); expect(s, same(stack)); })); @@ -119,7 +120,7 @@ void main() { Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isTrue); expect(result.isError, isFalse); - var value = result.asValue; + var value = result.asValue!; expect(value.value, equals(42)); }), onError: (e, s) { fail('Unexpected error: $e'); @@ -131,7 +132,7 @@ void main() { Result.capture(value).then(expectAsync1((Result result) { expect(result.isValue, isFalse); expect(result.isError, isTrue); - var error = result.asError; + var error = result.asError!; expect(error.error, equals('BAD')); expect(error.stackTrace, same(stack)); }), onError: (e, s) { @@ -153,7 +154,7 @@ void main() { var future = Future>.value(Result.error('BAD', stack)); Result.release(future).then((v) { fail('Unexpected value: $v'); - }, onError: expectAsync2((e, s) { + }).then((_) {}, onError: expectAsync2((e, s) { expect(e, equals('BAD')); expect(s, same(stack)); })); @@ -164,7 +165,7 @@ void main() { var future = Future>.error('BAD', stack); Result.release(future).then((v) { fail('Unexpected value: $v'); - }, onError: expectAsync2((e, s) { + }).then((_) {}, onError: expectAsync2((e, s) { expect(e, equals('BAD')); expect(s, same(stack)); })); @@ -203,15 +204,15 @@ void main() { expect(expectedList.isEmpty, isFalse); Result expected = expectedList.removeFirst(); expect(expected.isValue, isTrue); - expect(v, equals(expected.asValue.value)); + expect(v, equals(expected.asValue!.value)); } void errorListener(error, StackTrace stackTrace) { expect(expectedList.isEmpty, isFalse); Result expected = expectedList.removeFirst(); expect(expected.isError, isTrue); - expect(error, equals(expected.asError.error)); - expect(stackTrace, same(expected.asError.stackTrace)); + expect(error, equals(expected.asError!.error)); + expect(stackTrace, same(expected.asError!.stackTrace)); } stream.listen(expectAsync1(dataListener, count: 2), @@ -311,10 +312,10 @@ void expectResult(Result actual, Result expected) { expect(actual.isValue, equals(expected.isValue)); expect(actual.isError, equals(expected.isError)); if (actual.isValue) { - expect(actual.asValue.value, equals(expected.asValue.value)); + expect(actual.asValue!.value, equals(expected.asValue!.value)); } else { - expect(actual.asError.error, equals(expected.asError.error)); - expect(actual.asError.stackTrace, same(expected.asError.stackTrace)); + expect(actual.asError!.error, equals(expected.asError!.error)); + expect(actual.asError!.stackTrace, same(expected.asError!.stackTrace)); } } @@ -334,8 +335,8 @@ class TestSink implements EventSink { } @override - void addError(error, [StackTrace stack]) { - onError(error, stack); + void addError(error, [StackTrace? stack]) { + onError(error, stack ?? StackTrace.fromString('')); } @override diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 0cd210ac..ecb56215 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -77,7 +77,7 @@ void main() { var completer = StreamCompleter(); var lastEvent = -1; var controller = StreamController(); - StreamSubscription subscription; + late StreamSubscription subscription; subscription = completer.stream.listen((value) { expect(value, lessThan(3)); lastEvent = value; @@ -125,7 +125,7 @@ void main() { test("source stream isn't listened to until completer stream is", () async { var completer = StreamCompleter(); - StreamController controller; + late StreamController controller; controller = StreamController(onListen: () { scheduleMicrotask(controller.close); }); @@ -139,13 +139,13 @@ void main() { }); test('cancelOnError true when listening before linking stream', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); Object lastEvent = -1; - var controller = StreamController(); + var controller = StreamController(); completer.stream.listen((value) { expect(value, lessThan(3)); lastEvent = value; - }, onError: (value) { + }, onError: (Object value) { expect(value, '3'); lastEvent = value; }, onDone: unreachable('done'), cancelOnError: true); @@ -172,9 +172,9 @@ void main() { }); test('cancelOnError true when listening after linking stream', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); Object lastEvent = -1; - var controller = StreamController(); + var controller = StreamController(); completer.setSourceStream(controller.stream); controller.add(1); expect(controller.hasListener, isFalse); @@ -182,7 +182,7 @@ void main() { completer.stream.listen((value) { expect(value, lessThan(3)); lastEvent = value; - }, onError: (value) { + }, onError: (Object value) { expect(value, '3'); lastEvent = value; }, onDone: unreachable('done'), cancelOnError: true); @@ -265,8 +265,8 @@ void main() { }); test('setting onData etc. before and after setting stream', () async { - var completer = StreamCompleter(); - var controller = StreamController(); + var completer = StreamCompleter(); + var controller = StreamController(); var subscription = completer.stream.listen(null); Object lastEvent = 0; subscription.onData((value) => lastEvent = value); diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 690fbc17..eadae19a 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; void main() { group('single-subscription', () { - StreamGroup streamGroup; + late StreamGroup streamGroup; setUp(() { streamGroup = StreamGroup(); }); @@ -236,7 +236,7 @@ void main() { StreamController(onCancel: () => completer.future); var fired = false; - streamGroup.add(controller.stream).then((_) => fired = true); + streamGroup.add(controller.stream)!.then((_) => fired = true); await flushMicrotasks(); expect(fired, isFalse); @@ -249,7 +249,7 @@ void main() { }); group('broadcast', () { - StreamGroup streamGroup; + late StreamGroup streamGroup; setUp(() { streamGroup = StreamGroup.broadcast(); }); @@ -455,7 +455,7 @@ void main() { } void regardlessOfType(StreamGroup Function() newStreamGroup) { - StreamGroup streamGroup; + late StreamGroup streamGroup; setUp(() { streamGroup = newStreamGroup(); }); @@ -608,7 +608,7 @@ void regardlessOfType(StreamGroup Function() newStreamGroup) { await flushMicrotasks(); var fired = false; - streamGroup.remove(controller.stream).then((_) => fired = true); + streamGroup.remove(controller.stream)!.then((_) => fired = true); await flushMicrotasks(); expect(fired, isFalse); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index fa286a8e..74c91e66 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -250,7 +250,7 @@ void main() { var skip4 = events.skip(1); var index = 0; // Check that futures complete in order. - Func1Required sequence(expectedValue, sequenceIndex) => (value) { + Func1Required sequence(expectedValue, sequenceIndex) => (value) { expect(value, expectedValue); expect(index, sequenceIndex); index++; @@ -610,7 +610,7 @@ void main() { var controller = StreamController(); var events = StreamQueue(controller.stream); - bool hasNext; + bool? hasNext; events.hasNext.then((result) { hasNext = result; }); @@ -626,7 +626,7 @@ void main() { var controller = StreamController(); var events = StreamQueue(controller.stream); - bool hasNext; + bool? hasNext; events.hasNext.then((result) { hasNext = result; }); @@ -772,10 +772,10 @@ void main() { }); group('startTransaction operation produces a transaction that', () { - StreamQueue events; - StreamQueueTransaction transaction; - StreamQueue queue1; - StreamQueue queue2; + late StreamQueue events; + late StreamQueueTransaction transaction; + late StreamQueue queue1; + late StreamQueue queue2; setUp(() async { events = StreamQueue(createStream()); expect(await events.next, 1); @@ -1004,7 +1004,7 @@ void main() { }); group('withTransaction operation', () { - StreamQueue events; + late StreamQueue events; setUp(() async { events = StreamQueue(createStream()); expect(await events.next, 1); @@ -1058,7 +1058,7 @@ void main() { }); group('cancelable operation', () { - StreamQueue events; + late StreamQueue events; setUp(() async { events = StreamQueue(createStream()); expect(await events.next, 1); diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 69d1d8ad..591f32fc 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - StreamSinkCompleter completer; + late StreamSinkCompleter completer; setUp(() { completer = StreamSinkCompleter(); }); @@ -21,10 +21,10 @@ void main() { completer.setDestinationSink(sink); completer.sink..add(1)..add(2)..add(3)..add(4); - expect(sink.results[0].asValue.value, equals(1)); - expect(sink.results[1].asValue.value, equals(2)); - expect(sink.results[2].asValue.value, equals(3)); - expect(sink.results[3].asValue.value, equals(4)); + expect(sink.results[0].asValue!.value, equals(1)); + expect(sink.results[1].asValue!.value, equals(2)); + expect(sink.results[2].asValue!.value, equals(3)); + expect(sink.results[3].asValue!.value, equals(4)); }); test('error events are forwarded', () { @@ -32,8 +32,8 @@ void main() { completer.setDestinationSink(sink); completer.sink..addError('oh no')..addError("that's bad"); - expect(sink.results[0].asError.error, equals('oh no')); - expect(sink.results[1].asError.error, equals("that's bad")); + expect(sink.results[0].asError!.error, equals('oh no')); + expect(sink.results[1].asError!.error, equals("that's bad")); }); test('addStream is forwarded', () async { @@ -49,10 +49,10 @@ void main() { controller.addError("that's bad"); await flushMicrotasks(); - expect(sink.results[0].asValue.value, equals(1)); - expect(sink.results[1].asError.error, equals('oh no')); - expect(sink.results[2].asValue.value, equals(2)); - expect(sink.results[3].asError.error, equals("that's bad")); + expect(sink.results[0].asValue!.value, equals(1)); + expect(sink.results[1].asError!.error, equals('oh no')); + expect(sink.results[2].asValue!.value, equals(2)); + expect(sink.results[3].asError!.error, equals("that's bad")); expect(sink.isClosed, isFalse); controller.close(); @@ -121,10 +121,10 @@ void main() { completer.setDestinationSink(sink); await flushMicrotasks(); - expect(sink.results[0].asValue.value, equals(1)); - expect(sink.results[1].asValue.value, equals(2)); - expect(sink.results[2].asValue.value, equals(3)); - expect(sink.results[3].asValue.value, equals(4)); + expect(sink.results[0].asValue!.value, equals(1)); + expect(sink.results[1].asValue!.value, equals(2)); + expect(sink.results[2].asValue!.value, equals(3)); + expect(sink.results[3].asValue!.value, equals(4)); }); test('error events are forwarded', () async { @@ -135,8 +135,8 @@ void main() { completer.setDestinationSink(sink); await flushMicrotasks(); - expect(sink.results[0].asError.error, equals('oh no')); - expect(sink.results[1].asError.error, equals("that's bad")); + expect(sink.results[0].asError!.error, equals('oh no')); + expect(sink.results[1].asError!.error, equals("that's bad")); }); test('addStream is forwarded', () async { @@ -154,10 +154,10 @@ void main() { completer.setDestinationSink(sink); await flushMicrotasks(); - expect(sink.results[0].asValue.value, equals(1)); - expect(sink.results[1].asError.error, equals('oh no')); - expect(sink.results[2].asValue.value, equals(2)); - expect(sink.results[3].asError.error, equals("that's bad")); + expect(sink.results[0].asValue!.value, equals(1)); + expect(sink.results[1].asError!.error, equals('oh no')); + expect(sink.results[2].asValue!.value, equals(2)); + expect(sink.results[3].asError!.error, equals("that's bad")); expect(sink.isClosed, isFalse); }); @@ -258,9 +258,9 @@ void main() { futureCompleter.complete(testSink); await testSink.done; - expect(testSink.results[0].asValue.value, equals(1)); - expect(testSink.results[1].asValue.value, equals(2)); - expect(testSink.results[2].asValue.value, equals(3)); + expect(testSink.results[0].asValue!.value, equals(1)); + expect(testSink.results[1].asValue!.value, equals(2)); + expect(testSink.results[2].asValue!.value, equals(3)); }); test('with an error', () async { diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index 8493971d..e5f6baa1 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; import 'utils.dart'; void main() { - StreamController controller; + late StreamController controller; setUp(() { controller = StreamController(); }); @@ -18,7 +18,7 @@ void main() { group('fromStreamTransformer', () { test('transforms data events', () { var transformer = StreamSinkTransformer.fromStreamTransformer( - StreamTransformer.fromHandlers(handleData: (i, sink) { + StreamTransformer.fromHandlers(handleData: (int i, sink) { sink.add(i * 2); })); var sink = transformer.bind(controller.sink); @@ -117,7 +117,7 @@ void main() { group('fromHandlers', () { test('transforms data events', () { var transformer = - StreamSinkTransformer.fromHandlers(handleData: (i, sink) { + StreamSinkTransformer.fromHandlers(handleData: (int i, sink) { sink.add(i * 2); }); var sink = transformer.bind(controller.sink); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 3748c5ce..34931932 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -8,8 +8,8 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; void main() { - StreamController controller; - StreamSplitter splitter; + late StreamController controller; + late StreamSplitter splitter; setUp(() { controller = StreamController(); splitter = StreamSplitter(controller.stream); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 19c3decb..84948bff 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; /// Create an error with the same values as [base], except that it throwsA /// when seeing the value [errorValue]. -Stream streamError(Stream base, int errorValue, error) { +Stream streamError(Stream base, int errorValue, Object error) { return base.map((x) => (x == errorValue) ? throw error : x); } @@ -306,7 +306,7 @@ void main() { var s2 = Stream.fromIterable([1, 3, 5, 7]); var sz = StreamZip([s1, s2]); var ctr = 0; - StreamSubscription sub; + late StreamSubscription sub; sub = sz.listen(expectAsync1((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); if (ctr == 1) { diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index 68cd7235..50af6b62 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -33,7 +33,7 @@ void testStream(String name, StreamController controller, Stream stream) { var outer = Zone.current; runZoned(() { var newZone1 = Zone.current; - StreamSubscription sub; + late StreamSubscription sub; sub = stream.listen(expectAsync1((v) { expect(v, 42); expect(Zone.current, newZone1); diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 98a809f5..8be0572d 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -22,7 +22,7 @@ void main() { var stream = createStream(); var skips = 0; var completer = Completer(); - StreamSubscription subscription; + late StreamSubscription subscription; subscription = stream.listen((value) { ++skips; expect(value, skips); @@ -72,8 +72,9 @@ void main() { group('cancelOnError source:', () { for (var sourceCancels in [false, true]) { group('${sourceCancels ? "yes" : "no"}:', () { - SubscriptionStream subscriptionStream; - Future onCancel; // Completes if source stream is canceled before done. + late SubscriptionStream subscriptionStream; + late Future + onCancel; // Completes if source stream is canceled before done. setUp(() { var cancelCompleter = Completer(); var source = createErrorStream(cancelCompleter); @@ -159,7 +160,7 @@ Stream createStream() async* { yield 4; } -Stream createErrorStream([Completer onCancel]) async* { +Stream createErrorStream([Completer? onCancel]) async* { var canceled = true; try { yield 1; diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index f0e3a701..8278ea0c 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -90,7 +90,7 @@ void main() { subscriptionTransformer(handleCancel: expectAsync1((inner) { callbackInvoked = true; inner.cancel(); - return null; + return Future.value(); }))).listen(expectAsync1((_) {}, count: 0)); await flushMicrotasks(); @@ -248,11 +248,12 @@ void main() { }); group('when the outer subscription is canceled but the inner is not', () { - StreamSubscription subscription; + late StreamSubscription subscription; setUp(() { var controller = StreamController(); subscription = controller.stream - .transform(subscriptionTransformer(handleCancel: (_) => null)) + .transform( + subscriptionTransformer(handleCancel: (_) => Future.value())) .listen(expectAsync1((_) {}, count: 0), onError: expectAsync2((_, __) {}, count: 0), onDone: expectAsync0(() {}, count: 0)); diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index 2a115463..33d28a82 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -11,10 +11,11 @@ import '../utils.dart'; void main() { group('with valid types, forwards', () { - StreamController controller; - StreamSubscription wrapper; - bool isCanceled; + late StreamController controller; + late StreamSubscription wrapper; + late bool isCanceled; setUp(() { + isCanceled = false; controller = StreamController(onCancel: () { isCanceled = true; }); @@ -67,10 +68,11 @@ void main() { }); group('with invalid types,', () { - StreamController controller; - StreamSubscription wrapper; - bool isCanceled; + late StreamController controller; + late StreamSubscription wrapper; + late bool isCanceled; setUp(() { + isCanceled = false; controller = StreamController(onCancel: () { isCanceled = true; }); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index a99fe9b1..4cc743eb 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -25,7 +25,7 @@ OptionalArgAction unreachable(String name) => Matcher throwsZoned(matcher) => predicate((void Function() callback) { var firstError = true; runZoned(callback, - onError: expectAsync2((error, StackTrace stackTrace) { + onError: expectAsync2((Object error, StackTrace stackTrace) { if (firstError) { expect(error, matcher); firstError = false; @@ -37,11 +37,11 @@ Matcher throwsZoned(matcher) => predicate((void Function() callback) { }); /// A matcher that runs a callback in its own zone and asserts that that zone -/// emits a [CastError]. -final throwsZonedCastError = throwsZoned(TypeMatcher()); +/// emits a [TypeError]. +final throwsZonedCastError = throwsZoned(TypeMatcher()); -/// A matcher that matches a callback or future that throws a [CastError]. -final throwsCastError = throwsA(TypeMatcher()); +/// A matcher that matches a callback or future that throws a [TypeError]. +final throwsCastError = throwsA(TypeMatcher()); /// A badly behaved stream which throws if it's ever listened to. /// @@ -67,7 +67,7 @@ class CompleterStreamSink implements StreamSink { @override void add(T event) {} @override - void addError(error, [StackTrace stackTrace]) {} + void addError(error, [StackTrace? stackTrace]) {} @override Future addStream(Stream stream) async {} @override @@ -95,7 +95,7 @@ class TestSink implements StreamSink { /// /// If [onDone] is passed, it's called when the user calls [close]. Its result /// is piped to the [done] future. - TestSink({void Function() onDone}) : _onDone = onDone ?? (() {}); + TestSink({void Function()? onDone}) : _onDone = onDone ?? (() {}); @override void add(T event) { @@ -103,7 +103,7 @@ class TestSink implements StreamSink { } @override - void addError(error, [StackTrace stackTrace]) { + void addError(error, [StackTrace? stackTrace]) { results.add(Result.error(error, stackTrace)); } From 3b8fd87560c5bc6e3ce2e1422d6ebbcf29d42a57 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Tue, 14 Jul 2020 07:26:52 -0700 Subject: [PATCH 155/260] update collection/meta deps to the published versions (dart-lang/async#126) --- pkgs/async/pubspec.yaml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index bfdf20e1..b8b410ee 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -8,7 +8,7 @@ environment: sdk: '>=2.9.0-20.0.dev <2.9.0' dependencies: - collection: ^1.5.0 + collection: '>=1.15.0-nullsafety <1.15.0' dev_dependencies: fake_async: ^1.0.0 @@ -29,10 +29,6 @@ dependency_overrides: git: url: git://github.com/dart-lang/clock.git ref: null_safety - collection: - git: - url: git://github.com/dart-lang/collection.git - ref: null_safety fake_async: git: url: git://github.com/dart-lang/fake_async.git @@ -41,11 +37,7 @@ dependency_overrides: git: url: git://github.com/dart-lang/matcher.git ref: null_safety - meta: - git: - url: git://github.com/dart-lang/sdk.git - ref: null_safety-pkgs - path: pkg/meta + meta: '>=1.3.0-nullsafety <1.3.0' path: git: url: git://github.com/dart-lang/path.git From ca7d34a3480e6c9ffdd12e7824bd438619e70a62 Mon Sep 17 00:00:00 2001 From: William Hesse Date: Wed, 15 Jul 2020 15:56:33 +0200 Subject: [PATCH 156/260] Fix error in CancelableOperation.then (dart-lang/async#127) Replace a dynamic null check with a dynamic type cast, since the target type may be nullable. --- pkgs/async/lib/src/cancelable_operation.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 267c6d16..5d18dd31 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -103,7 +103,7 @@ class CancelableOperation { if (!completer.isCanceled) { if (isCompleted) { assert(result is T); - completer.complete(Future.sync(() => onValue(result!))); + completer.complete(Future.sync(() => onValue(result as T))); } else if (onCancel != null) { completer.complete(Future.sync(onCancel)); } else { From 14ac853fdc3adf71b558ec962921e5a75785bf1e Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Wed, 15 Jul 2020 08:40:33 -0700 Subject: [PATCH 157/260] add tests for .then with a nullable T (dart-lang/async#128) --- pkgs/async/test/cancelable_operation_test.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index c87e43a8..0e8d8d49 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -23,6 +23,13 @@ void main() { expect(completer.isCompleted, isTrue); }); + test('sends null values to the future', () { + expect(completer.operation.value, completion(equals(null))); + expect(completer.isCompleted, isFalse); + completer.complete(null); + expect(completer.isCompleted, isTrue); + }); + test('sends errors to the future', () { expect(completer.operation.value, throwsA('error')); expect(completer.isCompleted, isFalse); @@ -56,6 +63,11 @@ void main() { completer.completeError('error'); }); + test('chains null values through .then calls', () async { + var operation = CancelableOperation.fromFuture(Future.value(null)); + expect(await operation.then((_) {}).value, null); + }); + group('throws a StateError if completed', () { test('successfully twice', () { completer.complete(1); From 06f458edf5c6ec20856d87270c14814804571b80 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Tue, 21 Jul 2020 19:39:46 -0700 Subject: [PATCH 158/260] update for the 2.10 dev sdk (dart-lang/async#129) This is in preparation for the actual 2.10 dev sdk release. This package needs to be published and pinned in flutter simultaneously with that release. --- pkgs/async/.travis.yml | 10 ++--- pkgs/async/pubspec.yaml | 81 +++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 56 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index 0789302f..6058d0ee 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -1,23 +1,23 @@ language: dart dart: - - dev + - preview/raw/2.10.0-0.2-dev jobs: include: - stage: analyze_and_format name: "Analyzer" - dart: be/raw/latest + dart: preview/raw/2.10.0-0.2-dev os: linux script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . - stage: analyze_and_format name: "Format" - dart: be/raw/latest + dart: preview/raw/2.10.0-0.2-dev os: linux script: dartfmt -n --set-exit-if-changed . - stage: test name: "Vm Tests" - dart: be/raw/latest + dart: preview/raw/2.10.0-0.2-dev os: linux script: pub run --enable-experiment=non-nullable test -p vm @@ -27,7 +27,7 @@ stages: # Only building master means that we don't run two builds for each pull request. branches: - only: [master, null_safety] + only: [master] cache: directories: diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b8b410ee..dee5b394 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -5,83 +5,62 @@ description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async environment: - sdk: '>=2.9.0-20.0.dev <2.9.0' + # This must remain a tight constraint until nnbd is stable + sdk: '>=2.10.0-0 <2.10.0' dependencies: collection: '>=1.15.0-nullsafety <1.15.0' -dev_dependencies: - fake_async: ^1.0.0 - stack_trace: ^1.0.0 - test: ^1.0.0 - pedantic: ^1.0.0 - dependency_overrides: boolean_selector: - git: - url: git://github.com/dart-lang/boolean_selector.git - ref: null_safety + git: git://github.com/dart-lang/boolean_selector.git charcode: - git: - url: git://github.com/dart-lang/charcode.git - ref: null_safety - clock: - git: - url: git://github.com/dart-lang/clock.git - ref: null_safety - fake_async: - git: - url: git://github.com/dart-lang/fake_async.git - ref: null_safety + git: git://github.com/dart-lang/charcode.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/matcher.git - ref: null_safety - meta: '>=1.3.0-nullsafety <1.3.0' + url: git://github.com/dart-lang/sdk.git + path: pkg/meta + ref: 2-10-pkgs path: - git: - url: git://github.com/dart-lang/path.git - ref: null_safety + git: git://github.com/dart-lang/path.git pedantic: - git: - url: git://github.com/dart-lang/pedantic.git - ref: null_safety + git: git://github.com/dart-lang/pedantic.git pool: - git: - url: git://github.com/dart-lang/pool.git - ref: null_safety + 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: - url: git://github.com/dart-lang/source_span.git - ref: null_safety + git: git://github.com/dart-lang/source_span.git stack_trace: - git: - url: git://github.com/dart-lang/stack_trace.git - ref: null_safety + git: git://github.com/dart-lang/stack_trace.git stream_channel: - git: - url: git://github.com/dart-lang/stream_channel.git - ref: null_safety + git: git://github.com/dart-lang/stream_channel.git string_scanner: - git: - url: git://github.com/dart-lang/string_scanner.git - ref: null_safety + git: git://github.com/dart-lang/string_scanner.git term_glyph: - git: - url: git://github.com/dart-lang/term_glyph.git - ref: null_safety + git: git://github.com/dart-lang/term_glyph.git test_api: git: url: git://github.com/dart-lang/test.git - ref: null_safety path: pkgs/test_api test_core: git: url: git://github.com/dart-lang/test.git - ref: null_safety path: pkgs/test_core test: git: url: git://github.com/dart-lang/test.git - ref: null_safety path: pkgs/test + typed_data: + git: git://github.com/dart-lang/typed_data.git From afc0d729af274c639f980f8fdb84a78551bb84e0 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Wed, 22 Jul 2020 12:48:22 -0700 Subject: [PATCH 159/260] restore dev_dependencies and overrides of those (dart-lang/async#130) --- pkgs/async/pubspec.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index dee5b394..33e6486f 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -11,7 +11,23 @@ environment: dependencies: collection: '>=1.15.0-nullsafety <1.15.0' +dev_dependencies: + fake_async: ^1.0.0 + stack_trace: ^1.0.0 + test: ^1.0.0 + pedantic: ^1.0.0 + dependency_overrides: + # Dev dep overrides + clock: + git: + url: git://github.com/dart-lang/clock.git + ref: null_safety + fake_async: + git: + url: git://github.com/dart-lang/fake_async.git + ref: null_safety + # Normal test dep overrides boolean_selector: git: git://github.com/dart-lang/boolean_selector.git charcode: From d390263931d31cc709bc178661cc9cbe0f1a15c8 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Wed, 22 Jul 2020 21:38:32 -0700 Subject: [PATCH 160/260] CI: Test on dev branch (dart-lang/async#131) --- pkgs/async/.travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml index 6058d0ee..441b12e6 100644 --- a/pkgs/async/.travis.yml +++ b/pkgs/async/.travis.yml @@ -1,23 +1,20 @@ language: dart dart: - - preview/raw/2.10.0-0.2-dev + - dev jobs: include: - stage: analyze_and_format name: "Analyzer" - dart: preview/raw/2.10.0-0.2-dev os: linux script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . - stage: analyze_and_format name: "Format" - dart: preview/raw/2.10.0-0.2-dev os: linux script: dartfmt -n --set-exit-if-changed . - stage: test name: "Vm Tests" - dart: preview/raw/2.10.0-0.2-dev os: linux script: pub run --enable-experiment=non-nullable test -p vm From 75334af7977bfed01eaa62f9702e3301cc5210b4 Mon Sep 17 00:00:00 2001 From: Michael R Fairhurst Date: Sun, 23 Aug 2020 17:42:12 -0700 Subject: [PATCH 161/260] Remove unused dart:async import. (dart-lang/async#134) Since this version doesn't support Dart 2.0, this import is not necessary. --- pkgs/async/lib/src/result/future.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkgs/async/lib/src/result/future.dart b/pkgs/async/lib/src/result/future.dart index 20a5ebfd..a8dd3160 100644 --- a/pkgs/async/lib/src/result/future.dart +++ b/pkgs/async/lib/src/result/future.dart @@ -2,8 +2,6 @@ // 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. -import 'dart:async'; - import '../delegate/future.dart'; import 'result.dart'; From 34705a58dd2302dcb11cfb5d6dcdc2947d711f44 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 26 Aug 2020 10:01:33 -0700 Subject: [PATCH 162/260] Resolve null safety migration TODOS (dart-lang/async#135) - Switch from `StackTrace.fromString` to `AsyncError.defaultStackTrace`. - Remove a TODO about non-nullable `StackTrace`, by the time we merged null safety the type was already non-nullable and the TODO was stale. Style tweak - remove the argument names from a Function type that are redundant against their type names. --- pkgs/async/lib/src/result/error.dart | 3 +-- .../src/stream_sink_transformer/handler_transformer.dart | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index 27ce155d..9d0ba614 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -25,8 +25,7 @@ class ErrorResult implements Result { ErrorResult get asError => this; ErrorResult(this.error, [StackTrace? stackTrace]) - // TODO: Use AsyncError.defaultStackTrace(error) once available - : stackTrace = stackTrace ?? StackTrace.fromString(''); + : stackTrace = stackTrace ?? AsyncError.defaultStackTrace(error); @override void complete(Completer completer) { diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index b112e3ec..d6f188ec 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -11,11 +11,7 @@ import '../delegate/stream_sink.dart'; typedef HandleData = void Function(S data, EventSink sink); /// The type of the callback for handling error events. -// -// TODO: Update to take a non-nullable StackTrace once that change lands in -// the sdk. -typedef HandleError = void Function( - Object error, StackTrace stackTrace, EventSink sink); +typedef HandleError = void Function(Object error, StackTrace, EventSink); /// The type of the callback for handling done events. typedef HandleDone = void Function(EventSink sink); From 781f71d3d32af695aad6a6f38e6bd769f8e5d536 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 31 Aug 2020 10:29:06 -0700 Subject: [PATCH 163/260] Update git deps for merged packages (dart-lang/async#137) --- pkgs/async/pubspec.yaml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 33e6486f..0ff5f62a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -20,13 +20,9 @@ dev_dependencies: dependency_overrides: # Dev dep overrides clock: - git: - url: git://github.com/dart-lang/clock.git - ref: null_safety + git: git://github.com/dart-lang/clock.git fake_async: - git: - url: git://github.com/dart-lang/fake_async.git - ref: null_safety + git: git://github.com/dart-lang/fake_async.git # Normal test dep overrides boolean_selector: git: git://github.com/dart-lang/boolean_selector.git From 9cc2c71fe7530827f64ba2f609de9b4ba828296a Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 21 Sep 2020 14:00:55 +0200 Subject: [PATCH 164/260] Remove references to the deprecated CastError. (dart-lang/async#118) --- pkgs/async/lib/src/delegate/event_sink.dart | 2 +- pkgs/async/lib/src/delegate/future.dart | 2 +- pkgs/async/lib/src/delegate/sink.dart | 2 +- pkgs/async/lib/src/delegate/stream.dart | 2 +- pkgs/async/lib/src/delegate/stream_consumer.dart | 2 +- pkgs/async/lib/src/delegate/stream_sink.dart | 2 +- pkgs/async/lib/src/delegate/stream_subscription.dart | 2 +- pkgs/async/lib/src/stream_sink_transformer.dart | 2 +- pkgs/async/lib/src/typed_stream_transformer.dart | 2 +- pkgs/async/test/typed_wrapper/stream_subscription_test.dart | 6 +++--- pkgs/async/test/utils.dart | 4 ++-- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index 33d88e98..c011af5f 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -20,7 +20,7 @@ class DelegatingEventSink implements EventSink { /// /// Unlike [new DelegatingEventSink], this only requires its argument to be an /// instance of `EventSink`, not `EventSink`. This means that calls to - /// [add] may throw a [CastError] if the argument type doesn't match the + /// [add] may throw a [TypeError] if the argument type doesn't match the /// reified type of [sink]. @Deprecated( 'Use StreamController(sync: true)..stream.cast().pipe(sink)') diff --git a/pkgs/async/lib/src/delegate/future.dart b/pkgs/async/lib/src/delegate/future.dart index 11554523..2179de6f 100644 --- a/pkgs/async/lib/src/delegate/future.dart +++ b/pkgs/async/lib/src/delegate/future.dart @@ -16,7 +16,7 @@ class DelegatingFuture implements Future { /// /// This soundly converts a [Future] to a `Future`, regardless of its /// original generic type, by asserting that its value is an instance of `T` - /// whenever it's provided. If it's not, the future throws a [CastError]. + /// whenever it's provided. If it's not, the future throws a [TypeError]. @Deprecated('Use future.then((v) => v as T) instead.') static Future typed(Future future) => future is Future ? future : future.then((v) => v as T); diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index 57ef2a0d..e0b03c57 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -18,7 +18,7 @@ class DelegatingSink implements Sink { /// /// Unlike [new DelegatingSink], this only requires its argument to be an /// instance of `Sink`, not `Sink`. This means that calls to [add] may - /// throw a [CastError] if the argument type doesn't match the reified type of + /// throw a [TypeError] if the argument type doesn't match the reified type of /// [sink]. @Deprecated( 'Use StreamController(sync: true)..stream.cast().pipe(sink)') diff --git a/pkgs/async/lib/src/delegate/stream.dart b/pkgs/async/lib/src/delegate/stream.dart index 1585eab3..6c2031a4 100644 --- a/pkgs/async/lib/src/delegate/stream.dart +++ b/pkgs/async/lib/src/delegate/stream.dart @@ -20,7 +20,7 @@ class DelegatingStream extends StreamView { /// This soundly converts a [Stream] to a `Stream`, regardless of its /// original generic type, by asserting that its events are instances of `T` /// whenever they're provided. If they're not, the stream throws a - /// [CastError]. + /// [TypeError]. @Deprecated('Use stream.cast instead') static Stream typed(Stream stream) => stream.cast(); } diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index 6b92006d..0dad5ffa 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -20,7 +20,7 @@ class DelegatingStreamConsumer implements StreamConsumer { /// /// Unlike [new StreamConsumer], this only requires its argument to be an /// instance of `StreamConsumer`, not `StreamConsumer`. This means that - /// calls to [addStream] may throw a [CastError] if the argument type doesn't + /// calls to [addStream] may throw a [TypeError] if the argument type doesn't /// match the reified type of [consumer]. @Deprecated( 'Use StreamController(sync: true)..stream.cast().pipe(sink)') diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index ad76e904..f18d501f 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -23,7 +23,7 @@ class DelegatingStreamSink implements StreamSink { /// /// Unlike [new StreamSink], this only requires its argument to be an instance /// of `StreamSink`, not `StreamSink`. This means that calls to [add] may - /// throw a [CastError] if the argument type doesn't match the reified type of + /// throw a [TypeError] if the argument type doesn't match the reified type of /// [sink]. @Deprecated( 'Use StreamController(sync: true)..stream.cast().pipe(sink)') diff --git a/pkgs/async/lib/src/delegate/stream_subscription.dart b/pkgs/async/lib/src/delegate/stream_subscription.dart index 45b11343..581404a6 100644 --- a/pkgs/async/lib/src/delegate/stream_subscription.dart +++ b/pkgs/async/lib/src/delegate/stream_subscription.dart @@ -22,7 +22,7 @@ class DelegatingStreamSubscription implements StreamSubscription { /// This soundly converts a [StreamSubscription] to a `StreamSubscription`, /// regardless of its original generic type, by asserting that its events are /// instances of `T` whenever they're provided. If they're not, the - /// subscription throws a [CastError]. + /// subscription throws a [TypeError]. @Deprecated('Use Stream.cast instead') // TODO - Remove `TypeSafeStreamSubscription` and tests when removing this. static StreamSubscription typed(StreamSubscription subscription) => diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index bdcb1966..de48d39f 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -51,7 +51,7 @@ abstract class StreamSinkTransformer { /// This soundly converts a [StreamSinkTransformer] to a /// `StreamSinkTransformer`, regardless of its original generic type. /// This means that calls to [StreamSink.add] on the returned sink may throw a - /// [CastError] if the argument type doesn't match the reified type of the + /// [TypeError] if the argument type doesn't match the reified type of the /// sink. @deprecated // TODO remove TypeSafeStreamSinkTransformer diff --git a/pkgs/async/lib/src/typed_stream_transformer.dart b/pkgs/async/lib/src/typed_stream_transformer.dart index c1af93f9..8a392287 100644 --- a/pkgs/async/lib/src/typed_stream_transformer.dart +++ b/pkgs/async/lib/src/typed_stream_transformer.dart @@ -9,7 +9,7 @@ import 'dart:async'; /// This soundly converts a [StreamTransformer] to a `StreamTransformer`, /// regardless of its original generic type, by asserting that the events /// emitted by the transformed stream are instances of `T` whenever they're -/// provided. If they're not, the stream throws a [CastError]. +/// provided. If they're not, the stream throws a [TypeError]. @Deprecated('Use Stream.cast after binding a transformer instead') StreamTransformer typedStreamTransformer( StreamTransformer transformer) => diff --git a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart index 33d28a82..74195bab 100644 --- a/pkgs/async/test/typed_wrapper/stream_subscription_test.dart +++ b/pkgs/async/test/typed_wrapper/stream_subscription_test.dart @@ -79,7 +79,7 @@ void main() { wrapper = TypeSafeStreamSubscription(controller.stream.listen(null)); }); - group('throws a CastError for', () { + group('throws a TypeError for', () { test('onData()', () { expect(() { // TODO(nweiz): Use the wrapper declared in setUp when sdk#26226 is @@ -90,11 +90,11 @@ void main() { wrapper.onData(expectAsync1((_) {}, count: 0)); controller.add('foo'); - }, throwsZonedCastError); + }, throwsZonedTypeError); }); }); - group("doesn't throw a CastError for", () { + group("doesn't throw a TypeError for", () { test('onError()', () { wrapper.onError(expectAsync1((error) { expect(error, equals('oh no')); diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 4cc743eb..2ddecc4a 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -38,10 +38,10 @@ Matcher throwsZoned(matcher) => predicate((void Function() callback) { /// A matcher that runs a callback in its own zone and asserts that that zone /// emits a [TypeError]. -final throwsZonedCastError = throwsZoned(TypeMatcher()); +final throwsZonedTypeError = throwsZoned(TypeMatcher()); /// A matcher that matches a callback or future that throws a [TypeError]. -final throwsCastError = throwsA(TypeMatcher()); +final throwsTypeError = throwsA(TypeMatcher()); /// A badly behaved stream which throws if it's ever listened to. /// From 178692bc58c09b7b64f0f49d0f0fda63e2a5f565 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 22 Sep 2020 08:36:52 -0700 Subject: [PATCH 165/260] Prepare for the 2.11 dev SDKs (dart-lang/async#138) Bump the upper bound to allow 2.10 stable and 2.11.0 dev SDK versions. --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 3ca7f6c2..7edafcd9 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.0-nullsafety.1 + +* Allow 2.10 stable and 2.11.0 dev SDK versions. + ## 2.5.0-nullsafety * Migrate this package to null safety. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0ff5f62a..cc20a254 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,12 +1,12 @@ name: async -version: 2.5.0-nullsafety +version: 2.5.0-nullsafety.1 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/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: collection: '>=1.15.0-nullsafety <1.15.0' From 8dfbb1d39907c9f2985ff897a46166e2a774ccbf Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 25 Sep 2020 10:40:25 -0700 Subject: [PATCH 166/260] Avoid a `late final` field without initializer (dart-lang/async#139) This had been set in the constructor because it needs to reference `this`. A new capability with `late final` files is to allow an initializer expression referencing `this`. This approach also avoid introducing a setter to the public API. The behavior difference is that the `CancelableOperation` wont be instantiated until it is read, but this is fine because `CancelableOperation` does not have any side effects during initialization and it's only field is the `CancelableCompleter`. Technically it's statically breaking to remove the setter, but since it could never be called at runtime this is safe to do. Also initialize the `_inner` field where it is defined rather than in an initializer list since there are not multiple constructors that would assign different values. --- pkgs/async/CHANGELOG.md | 5 +++++ pkgs/async/lib/src/cancelable_operation.dart | 10 +++------- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 7edafcd9..cb768957 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.5.0-nullsafety.2-dev + +* Remove the unusable setter `CancelableOperation.operation=`. This was + mistakenly added to the public API but could never be called. + ## 2.5.0-nullsafety.1 * Allow 2.10 stable and 2.11.0 dev SDK versions. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 5d18dd31..c3210787 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -143,7 +143,7 @@ class CancelableOperation { /// A completer for a [CancelableOperation]. class CancelableCompleter { /// The completer for the wrapped future. - final Completer _inner; + final _inner = Completer(); /// The callback to call if the future is canceled. final FutureOrCallback? _onCancel; @@ -156,14 +156,10 @@ class CancelableCompleter { /// /// [onCancel] will be called synchronously when the operation is canceled. /// It's guaranteed to only be called once. - CancelableCompleter({FutureOr Function()? onCancel}) - : _onCancel = onCancel, - _inner = Completer() { - operation = CancelableOperation._(this); - } + CancelableCompleter({FutureOr Function()? onCancel}) : _onCancel = onCancel; /// The operation controlled by this completer. - late final CancelableOperation operation; + late final operation = CancelableOperation._(this); /// Whether the completer has completed. bool get isCompleted => _isCompleted; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index cc20a254..64da9c45 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.5.0-nullsafety.1 +version: 2.5.0-nullsafety.2-dev description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 79f9dc49606f892b9acb37348978c2f44cabbb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bencze=20Bal=C3=A1zs?= Date: Thu, 22 Oct 2020 17:51:57 +0300 Subject: [PATCH 167/260] Invalidate async cache on exception (dart-lang/async#132) * Invalidate async cache on exception When the callback throws an exception, the startStaleTime was not called, so the cache was not refreshed. Because of this, future calls to this cache returned with the exception. --- pkgs/async/lib/src/async_cache.dart | 8 ++++---- pkgs/async/test/async_cache_test.dart | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index d6f5f7f8..99b3881f 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -60,12 +60,12 @@ class AsyncCache { if (_cachedStreamSplitter != null) { throw StateError('Previously used to cache via `fetchStream`'); } - if (_cachedValueFuture == null) { - _cachedValueFuture = callback(); - await _cachedValueFuture; + final result = _cachedValueFuture ??= callback(); + try { + return await result; + } finally { _startStaleTimer(); } - return _cachedValueFuture!; } /// Returns a cached stream from a previous call to [fetchStream], or runs diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 81e43e17..3ceda792 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -158,4 +158,16 @@ void main() { })); expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); }); + + test('should invalidate even if the future throws an exception', () async { + cache = AsyncCache.ephemeral(); + + Future throwingCall() async => throw Exception(); + await expectLater(cache.fetch(throwingCall), throwsA(isException)); + // To let the timer invalidate the cache + await Future.delayed(Duration(milliseconds: 5)); + + Future call() async => 'Completed'; + expect(await cache.fetch(call), 'Completed', reason: 'Cache invalidates'); + }); } From 7ccdd587640d764e565fee7b94d6aea89789d7f3 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 23 Oct 2020 09:37:06 -0700 Subject: [PATCH 168/260] Allow 2.12.0 dev SDK versions (dart-lang/async#141) Drop dependency overrides and update deps to `-nullsafety` versions. --- pkgs/async/CHANGELOG.md | 3 +- pkgs/async/pubspec.yaml | 74 ++++------------------------------------- 2 files changed, 9 insertions(+), 68 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index cb768957..e63511b6 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,7 +1,8 @@ -## 2.5.0-nullsafety.2-dev +## 2.5.0-nullsafety.2 * Remove the unusable setter `CancelableOperation.operation=`. This was mistakenly added to the public API but could never be called. +* Allow 2.12.0 dev SDK versions. ## 2.5.0-nullsafety.1 diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 64da9c45..ce72156d 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,78 +1,18 @@ name: async -version: 2.5.0-nullsafety.2-dev +version: 2.5.0-nullsafety.2 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/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: collection: '>=1.15.0-nullsafety <1.15.0' -dev_dependencies: - fake_async: ^1.0.0 - stack_trace: ^1.0.0 - test: ^1.0.0 - pedantic: ^1.0.0 - -dependency_overrides: - # Dev dep overrides - clock: - git: git://github.com/dart-lang/clock.git - fake_async: - git: git://github.com/dart-lang/fake_async.git - # Normal test dep overrides - boolean_selector: - git: git://github.com/dart-lang/boolean_selector.git - charcode: - git: git://github.com/dart-lang/charcode.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 +dev_dependencies: + fake_async: ^1.2.0-nullsafety + stack_trace: ^1.10.0-nullsafety + test: ^1.16.0-nullsafety + pedantic: ^1.10.0-nullsafety From 54908bf3082ed76601f00fea4858cb2c835420cb Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 2 Nov 2020 12:12:31 -0800 Subject: [PATCH 169/260] Migrate to runZonedGuarded (dart-lang/async#142) The `onError` argument to `runZoned` is deprecated. Switch to the supported `runZonedGuarded`. Remove argument types on the function literal since thy can now be inferred. `runZonedGuarded` has a specific function type argument, whereas `onError` was typed as `Function` which did not allow inference on argument types. --- pkgs/async/CHANGELOG.md | 2 ++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/utils.dart | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index e63511b6..549c758b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.5.0-nullsafety.3-dev + ## 2.5.0-nullsafety.2 * Remove the unusable setter `CancelableOperation.operation=`. This was diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index ce72156d..5f87f9b4 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.5.0-nullsafety.2 +version: 2.5.0-nullsafety.3-dev description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 2ddecc4a..b7e64e2f 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -19,13 +19,13 @@ typedef OptionalArgAction = void Function([dynamic a, dynamic b]); OptionalArgAction unreachable(String name) => ([a, b]) => fail('Unreachable: $name'); -// TODO(nweiz): Use the version of this in test when test#418 is fixed. /// A matcher that runs a callback in its own zone and asserts that that zone /// emits an error that matches [matcher]. Matcher throwsZoned(matcher) => predicate((void Function() callback) { var firstError = true; - runZoned(callback, - onError: expectAsync2((Object error, StackTrace stackTrace) { + runZonedGuarded( + callback, + expectAsync2((error, stackTrace) { if (firstError) { expect(error, matcher); firstError = false; From 89376f39e83b56688d2f3c45feb6886f5144d60a Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 3 Nov 2020 14:12:05 -0800 Subject: [PATCH 170/260] Bump SDK constraints for pub (dart-lang/async#143) 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/async/CHANGELOG.md | 5 ++++- pkgs/async/pubspec.yaml | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 549c758b..d2d99c35 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,7 @@ -## 2.5.0-nullsafety.3-dev +## 2.5.0-nullsafety.3 + +* Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release + guidelines. ## 2.5.0-nullsafety.2 diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 5f87f9b4..3aa80807 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,12 +1,11 @@ name: async -version: 2.5.0-nullsafety.3-dev +version: 2.5.0-nullsafety.3 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/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: collection: '>=1.15.0-nullsafety <1.15.0' From 43e4a96d87c3b2920133203a777143bb3794b23f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 6 Nov 2020 18:19:41 +0100 Subject: [PATCH 171/260] Various clean-ups and null-safety improvements. (dart-lang/async#140) --- pkgs/async/CHANGELOG.md | 2 + pkgs/async/lib/src/async_cache.dart | 2 +- pkgs/async/lib/src/result/result.dart | 5 +-- .../src/single_subscription_transformer.dart | 7 ++-- pkgs/async/lib/src/stream_group.dart | 23 ++++++------ pkgs/async/lib/src/stream_zip.dart | 37 ++++++++++--------- pkgs/async/lib/src/subscription_stream.dart | 10 ++--- pkgs/async/pubspec.yaml | 2 +- 8 files changed, 46 insertions(+), 42 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d2d99c35..ab5a57dc 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.5.0-nullsafety.4-dev + ## 2.5.0-nullsafety.3 * Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 99b3881f..c4cd04e1 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -42,7 +42,7 @@ class AsyncCache { /// The [duration] starts counting after the Future returned by [fetch] /// completes, or after the Stream returned by [fetchStream] emits a done /// event. - AsyncCache(this._duration); + AsyncCache(Duration duration) : _duration = duration; /// Creates a cache that invalidates after an in-flight request is complete. /// diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 5f93b551..165b4725 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -99,7 +99,7 @@ abstract class Result { static Future>> captureAll(Iterable> elements) { var results = ?>[]; var pending = 0; - late Completer>> completer; + var completer = Completer>>(); for (var element in elements) { if (element is Future) { var i = results.length; @@ -116,9 +116,8 @@ abstract class Result { } } if (pending == 0) { - return Future.value(List.from(results)); + completer.complete(List.from(results)); } - completer = Completer>>(); return completer.future; } diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index 2e056b2f..7c93b449 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -18,10 +18,8 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { @override Stream bind(Stream stream) { - late StreamSubscription subscription; - var controller = - StreamController(sync: true, onCancel: () => subscription.cancel()); - subscription = stream.listen((value) { + var controller = StreamController(sync: true); + var subscription = stream.listen((value) { // TODO(nweiz): When we release a new major version, get rid of the second // type parameter and avoid this conversion. try { @@ -30,6 +28,7 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { controller.addError(error, stackTrace); } }, onError: controller.addError, onDone: controller.close); + controller.onCancel = subscription.cancel; return controller.stream; } } diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 8c17ec20..14a04e50 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -29,7 +29,7 @@ import 'dart:async'; class StreamGroup implements Sink> { /// The stream through which all events from streams in the group are emitted. Stream get stream => _controller.stream; - late StreamController _controller; + final StreamController _controller; /// Whether the group is closed, meaning that no more streams may be added. var _closed = false; @@ -72,19 +72,20 @@ class StreamGroup implements Sink> { } /// Creates a new stream group where [stream] is single-subscriber. - StreamGroup() { - _controller = StreamController( - onListen: _onListen, - onPause: _onPause, - onResume: _onResume, - onCancel: _onCancel, - sync: true); + StreamGroup() : _controller = StreamController(sync: true) { + _controller + ..onListen = _onListen + ..onPause = _onPause + ..onResume = _onResume + ..onCancel = _onCancel; } /// Creates a new stream group where [stream] is a broadcast stream. - StreamGroup.broadcast() { - _controller = StreamController.broadcast( - onListen: _onListen, onCancel: _onCancelBroadcast, sync: true); + StreamGroup.broadcast() + : _controller = StreamController.broadcast(sync: true) { + _controller + ..onListen = _onListen + ..onCancel = _onCancelBroadcast; } /// Adds [stream] as a member of this group. diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index 506bf6d9..e417cdc9 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -22,7 +22,7 @@ class StreamZip extends Stream> { {Function? onError, void Function()? onDone, bool? cancelOnError}) { cancelOnError = identical(true, cancelOnError); var subscriptions = >[]; - late StreamController> controller; + var controller = StreamController>(); late List current; var dataCount = 0; @@ -32,7 +32,7 @@ class StreamZip extends Stream> { dataCount++; if (dataCount == subscriptions.length) { var data = List.from(current); - current = List.filled(subscriptions.length, null); + current.fillRange(0, current.length, null); dataCount = 0; for (var i = 0; i < subscriptions.length; i++) { if (i != index) subscriptions[i].resume(); @@ -87,23 +87,26 @@ class StreamZip extends Stream> { current = List.filled(subscriptions.length, null); - controller = StreamController>(onPause: () { - for (var i = 0; i < subscriptions.length; i++) { - // This may pause some subscriptions more than once. - // These will not be resumed by onResume below, but must wait for the - // next round. - subscriptions[i].pause(); - } - }, onResume: () { - for (var i = 0; i < subscriptions.length; i++) { - subscriptions[i].resume(); + controller + ..onPause = () { + for (var i = 0; i < subscriptions.length; i++) { + // This may pause some subscriptions more than once. + // These will not be resumed by onResume below, but must wait for the + // next round. + subscriptions[i].pause(); + } } - }, onCancel: () { - for (var i = 0; i < subscriptions.length; i++) { - // Canceling more than once is safe. - subscriptions[i].cancel(); + ..onResume = () { + for (var i = 0; i < subscriptions.length; i++) { + subscriptions[i].resume(); + } } - }); + ..onCancel = () { + for (var i = 0; i < subscriptions.length; i++) { + // Canceling more than once is safe. + subscriptions[i].cancel(); + } + }; if (subscriptions.isEmpty) { controller.close(); diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 2c94e05d..501cf917 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -31,12 +31,12 @@ class SubscriptionStream extends Stream { /// an error. SubscriptionStream(StreamSubscription subscription) : _source = subscription { - var source = _source!; - source.pause(); // Clear callbacks to avoid keeping them alive unnecessarily. - source.onData(null); - source.onError(null); - source.onDone(null); + subscription + ..pause() + ..onData(null) + ..onError(null) + ..onDone(null); } @override diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3aa80807..671174f5 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.5.0-nullsafety.3 +version: 2.5.0-nullsafety.4-dev description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 54c64c3fe0b89b53f9608489a3afd42cbde4ecaa Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 12 Nov 2020 17:42:47 -0800 Subject: [PATCH 172/260] Delete .test_config (dart-lang/async#144) --- pkgs/async/.test_config | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 pkgs/async/.test_config diff --git a/pkgs/async/.test_config b/pkgs/async/.test_config deleted file mode 100644 index 412fc5c5..00000000 --- a/pkgs/async/.test_config +++ /dev/null @@ -1,3 +0,0 @@ -{ - "test_package": true -} \ No newline at end of file From 52bff1a70954aaf5622e8d25540ab4a5eb1e9341 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Mon, 21 Dec 2020 22:47:36 +0100 Subject: [PATCH 173/260] Migrate to GitHub Actions (dart-lang/async#149) * Migrate to GitHub Actions * Delete .travis.yml * Analyze with `--fatal-infos` --- pkgs/async/.github/workflows/test-package.yml | 61 +++++++++++++++++++ pkgs/async/.travis.yml | 31 ---------- 2 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 pkgs/async/.github/workflows/test-package.yml delete mode 100644 pkgs/async/.travis.yml diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml new file mode 100644 index 00000000..1cd8438b --- /dev/null +++ b/pkgs/async/.github/workflows/test-package.yml @@ -0,0 +1,61 @@ +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' diff --git a/pkgs/async/.travis.yml b/pkgs/async/.travis.yml deleted file mode 100644 index 441b12e6..00000000 --- a/pkgs/async/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -language: dart - -dart: - - dev - -jobs: - include: - - stage: analyze_and_format - name: "Analyzer" - os: linux - script: dartanalyzer --enable-experiment=non-nullable --fatal-warnings --fatal-infos . - - stage: analyze_and_format - name: "Format" - os: linux - script: dartfmt -n --set-exit-if-changed . - - stage: test - name: "Vm Tests" - os: linux - script: pub run --enable-experiment=non-nullable test -p vm - -stages: - - analyze_and_format - - test - -# Only building master means that we don't run two builds for each pull request. -branches: - only: [master] - -cache: - directories: - - $HOME/.pub-cache From de690c34eabdacdf7581727c01e052f82abfeec9 Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Tue, 19 Jan 2021 16:40:24 -0800 Subject: [PATCH 174/260] Add package of the week video (dart-lang/async#150) --- pkgs/async/lib/async.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 2af006b0..e1faaf1a 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -2,6 +2,11 @@ // 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. +/// Utilities that expand on the asynchronous features of the `dart:async` library. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=r0tHiCjW2w0} +library pkg.async; + export 'src/async_cache.dart'; export 'src/async_memoizer.dart'; export 'src/byte_collector.dart'; From c6418c3d729563cc67a78937433e9a8f1ac7ae8b Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 29 Jan 2021 10:13:38 -0800 Subject: [PATCH 175/260] Revert "Various clean-ups and null-safety improvements. (dart-lang/async#140)" (dart-lang/async#152) This reverts commit 43e4a96d87c3b2920133203a777143bb3794b23f. Since this has not been published or rolled through to the SDK or Flutter we will hold it back to reduce risk. --- pkgs/async/CHANGELOG.md | 2 - pkgs/async/lib/src/async_cache.dart | 2 +- pkgs/async/lib/src/result/result.dart | 5 ++- .../src/single_subscription_transformer.dart | 7 ++-- pkgs/async/lib/src/stream_group.dart | 23 ++++++------ pkgs/async/lib/src/stream_zip.dart | 37 +++++++++---------- pkgs/async/lib/src/subscription_stream.dart | 10 ++--- pkgs/async/pubspec.yaml | 2 +- 8 files changed, 42 insertions(+), 46 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index ab5a57dc..d2d99c35 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,3 @@ -## 2.5.0-nullsafety.4-dev - ## 2.5.0-nullsafety.3 * Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index c4cd04e1..99b3881f 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -42,7 +42,7 @@ class AsyncCache { /// The [duration] starts counting after the Future returned by [fetch] /// completes, or after the Stream returned by [fetchStream] emits a done /// event. - AsyncCache(Duration duration) : _duration = duration; + AsyncCache(this._duration); /// Creates a cache that invalidates after an in-flight request is complete. /// diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 165b4725..5f93b551 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -99,7 +99,7 @@ abstract class Result { static Future>> captureAll(Iterable> elements) { var results = ?>[]; var pending = 0; - var completer = Completer>>(); + late Completer>> completer; for (var element in elements) { if (element is Future) { var i = results.length; @@ -116,8 +116,9 @@ abstract class Result { } } if (pending == 0) { - completer.complete(List.from(results)); + return Future.value(List.from(results)); } + completer = Completer>>(); return completer.future; } diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index 7c93b449..2e056b2f 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -18,8 +18,10 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { @override Stream bind(Stream stream) { - var controller = StreamController(sync: true); - var subscription = stream.listen((value) { + late StreamSubscription subscription; + var controller = + StreamController(sync: true, onCancel: () => subscription.cancel()); + subscription = stream.listen((value) { // TODO(nweiz): When we release a new major version, get rid of the second // type parameter and avoid this conversion. try { @@ -28,7 +30,6 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { controller.addError(error, stackTrace); } }, onError: controller.addError, onDone: controller.close); - controller.onCancel = subscription.cancel; return controller.stream; } } diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 14a04e50..8c17ec20 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -29,7 +29,7 @@ import 'dart:async'; class StreamGroup implements Sink> { /// The stream through which all events from streams in the group are emitted. Stream get stream => _controller.stream; - final StreamController _controller; + late StreamController _controller; /// Whether the group is closed, meaning that no more streams may be added. var _closed = false; @@ -72,20 +72,19 @@ class StreamGroup implements Sink> { } /// Creates a new stream group where [stream] is single-subscriber. - StreamGroup() : _controller = StreamController(sync: true) { - _controller - ..onListen = _onListen - ..onPause = _onPause - ..onResume = _onResume - ..onCancel = _onCancel; + StreamGroup() { + _controller = StreamController( + onListen: _onListen, + onPause: _onPause, + onResume: _onResume, + onCancel: _onCancel, + sync: true); } /// Creates a new stream group where [stream] is a broadcast stream. - StreamGroup.broadcast() - : _controller = StreamController.broadcast(sync: true) { - _controller - ..onListen = _onListen - ..onCancel = _onCancelBroadcast; + StreamGroup.broadcast() { + _controller = StreamController.broadcast( + onListen: _onListen, onCancel: _onCancelBroadcast, sync: true); } /// Adds [stream] as a member of this group. diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index e417cdc9..506bf6d9 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -22,7 +22,7 @@ class StreamZip extends Stream> { {Function? onError, void Function()? onDone, bool? cancelOnError}) { cancelOnError = identical(true, cancelOnError); var subscriptions = >[]; - var controller = StreamController>(); + late StreamController> controller; late List current; var dataCount = 0; @@ -32,7 +32,7 @@ class StreamZip extends Stream> { dataCount++; if (dataCount == subscriptions.length) { var data = List.from(current); - current.fillRange(0, current.length, null); + current = List.filled(subscriptions.length, null); dataCount = 0; for (var i = 0; i < subscriptions.length; i++) { if (i != index) subscriptions[i].resume(); @@ -87,26 +87,23 @@ class StreamZip extends Stream> { current = List.filled(subscriptions.length, null); - controller - ..onPause = () { - for (var i = 0; i < subscriptions.length; i++) { - // This may pause some subscriptions more than once. - // These will not be resumed by onResume below, but must wait for the - // next round. - subscriptions[i].pause(); - } + controller = StreamController>(onPause: () { + for (var i = 0; i < subscriptions.length; i++) { + // This may pause some subscriptions more than once. + // These will not be resumed by onResume below, but must wait for the + // next round. + subscriptions[i].pause(); } - ..onResume = () { - for (var i = 0; i < subscriptions.length; i++) { - subscriptions[i].resume(); - } + }, onResume: () { + for (var i = 0; i < subscriptions.length; i++) { + subscriptions[i].resume(); } - ..onCancel = () { - for (var i = 0; i < subscriptions.length; i++) { - // Canceling more than once is safe. - subscriptions[i].cancel(); - } - }; + }, onCancel: () { + for (var i = 0; i < subscriptions.length; i++) { + // Canceling more than once is safe. + subscriptions[i].cancel(); + } + }); if (subscriptions.isEmpty) { controller.close(); diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 501cf917..2c94e05d 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -31,12 +31,12 @@ class SubscriptionStream extends Stream { /// an error. SubscriptionStream(StreamSubscription subscription) : _source = subscription { + var source = _source!; + source.pause(); // Clear callbacks to avoid keeping them alive unnecessarily. - subscription - ..pause() - ..onData(null) - ..onError(null) - ..onDone(null); + source.onData(null); + source.onError(null); + source.onDone(null); } @override diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 671174f5..3aa80807 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.5.0-nullsafety.4-dev +version: 2.5.0-nullsafety.3 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async From 9dde9dc1e905f839373a11f91ab1de66496bf682 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 2 Feb 2021 11:16:34 -0800 Subject: [PATCH 176/260] Prepare to publish stable null safety (dart-lang/async#153) --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d2d99c35..d43d7176 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.0 + +* Stable release for null safety. + ## 2.5.0-nullsafety.3 * Update SDK constraints to `>=2.12.0-0 <3.0.0` based on beta release diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3aa80807..3c8dd95b 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.5.0-nullsafety.3 +version: 2.5.0 description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async @@ -8,7 +8,7 @@ environment: sdk: ">=2.12.0-0 <3.0.0" dependencies: - collection: '>=1.15.0-nullsafety <1.15.0' + collection: ^1.15.0 dev_dependencies: fake_async: ^1.2.0-nullsafety From a51466c7fd6d19659a1879bf8ffaefde4e2e277a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 8 Mar 2021 09:06:40 -0800 Subject: [PATCH 177/260] Fix new pedantic lints, test on oldest supported SDK (dart-lang/async#155) Use v1 setup-dart --- pkgs/async/.github/workflows/test-package.yml | 10 ++--- pkgs/async/CHANGELOG.md | 2 + pkgs/async/pubspec.yaml | 6 +-- .../test/result/result_captureAll_test.dart | 42 ++++++++++--------- 4 files changed, 33 insertions(+), 27 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 1cd8438b..e47bf660 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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 diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d43d7176..c7256ac1 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.5.1-dev + ## 2.5.0 * Stable release for null safety. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 3c8dd95b..e24947d5 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,11 +1,11 @@ name: async -version: 2.5.0 +version: 2.5.1-dev description: Utility functions and classes related to the 'dart:async' library. -homepage: https://www.github.com/dart-lang/async +repository: https://github.com/dart-lang/async environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0 <3.0.0" dependencies: collection: ^1.15.0 diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index b992e1f0..c00395a5 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -9,7 +9,9 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; final someStack = StackTrace.current; + Result res(int n) => Result.value(n); + Result err(n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of futures. @@ -63,11 +65,11 @@ void main() { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); - await 0; + await _microTask(); cs[0].complete(1); - await 0; + await _microTask(); cs[1].complete(2); - await 0; + await _microTask(); cs[2].completeError('3', someStack); }); @@ -75,11 +77,11 @@ void main() { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); - await 0; + await _microTask(); cs[0].complete(1); - await 0; + await _microTask(); cs[2].completeError('3', someStack); - await 0; + await _microTask(); cs[1].complete(2); }); @@ -87,11 +89,11 @@ void main() { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); - await 0; + await _microTask(); cs[1].complete(2); - await 0; + await _microTask(); cs[0].complete(1); - await 0; + await _microTask(); cs[2].completeError('3', someStack); }); @@ -99,11 +101,11 @@ void main() { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); - await 0; + await _microTask(); cs[1].complete(2); - await 0; + await _microTask(); cs[2].completeError('3', someStack); - await 0; + await _microTask(); cs[0].complete(1); }); @@ -111,11 +113,11 @@ void main() { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); - await 0; + await _microTask(); cs[2].completeError('3', someStack); - await 0; + await _microTask(); cs[0].complete(1); - await 0; + await _microTask(); cs[1].complete(2); }); @@ -123,11 +125,11 @@ void main() { var cs = List.generate(3, (_) => Completer()); var all = Result.captureAll(cs.map((c) => c.future)); expect(all, completion([res(1), res(2), err(3)])); - await 0; + await _microTask(); cs[2].completeError('3', someStack); - await 0; + await _microTask(); cs[1].complete(2); - await 0; + await _microTask(); cs[0].complete(1); }); @@ -150,7 +152,7 @@ void main() { }); completeFunctions.shuffle(rnd); for (var i = 0; i < n; i++) { - await 0; + await _microTask(); completeFunctions[i](); } }); @@ -187,3 +189,5 @@ void main() { }); }); } + +Future _microTask() => Future.microtask(() {}); From 723df4e979be420b9f47362b87a69a334f9c2678 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 17 Mar 2021 12:09:21 -0700 Subject: [PATCH 178/260] Use the name that matches no library directive (dart-lang/async#158) Fixes dart-lang/async#156 The readme, and likely other documentation around the web, has links to the dartdoc site using the library name "async", which is what the library would be named in the docs if we omitted the `library` directive entirely. It is easier to restore the old paths by naming the library identically than to update all the links. --- pkgs/async/lib/async.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index e1faaf1a..a97fc65e 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -5,7 +5,7 @@ /// Utilities that expand on the asynchronous features of the `dart:async` library. /// /// {@youtube 560 315 https://www.youtube.com/watch?v=r0tHiCjW2w0} -library pkg.async; +library async; export 'src/async_cache.dart'; export 'src/async_memoizer.dart'; From 8605ecc021d1be5ccf28a22f24b0d1bd5d059dec Mon Sep 17 00:00:00 2001 From: mnordine Date: Fri, 26 Mar 2021 17:25:24 -0300 Subject: [PATCH 179/260] Fix typo (dart-lang/async#159) --- pkgs/async/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/README.md b/pkgs/async/README.md index 359b93a1..f7ed8beb 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -23,7 +23,7 @@ computations. [`DelegatingStreamSink`][DelegatingStreamSink]. * The [`FutureGroup`][FutureGroup] class makes it easy to wait until a group of - features that may change over time completes. + futures that may change over time completes. * The [`LazyStream`][LazyStream] class allows a stream to be initialized lazily when `.listen()` is first called. From 58df1ecb48ebe3c90627cc54e945997da3b95db6 Mon Sep 17 00:00:00 2001 From: Franklin Yow <58489007+franklinyow@users.noreply.github.com> Date: Thu, 1 Apr 2021 16:58:49 -0700 Subject: [PATCH 180/260] Update LICENSE (dart-lang/async#162) Changes to comply with internal review --- pkgs/async/LICENSE | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/async/LICENSE b/pkgs/async/LICENSE index de31e1a0..dbd2843a 100644 --- a/pkgs/async/LICENSE +++ b/pkgs/async/LICENSE @@ -1,4 +1,5 @@ -Copyright 2015, the Dart project authors. All rights reserved. +Copyright 2015, the Dart project authors. + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -9,7 +10,7 @@ met: copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. From 79694fc75e67df1e7d840a9decf5b754213a6cfb Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Fri, 16 Apr 2021 22:22:36 +0200 Subject: [PATCH 181/260] Add ChunkedStreamReader (dart-lang/async#161) * Added ChunkedStreamReader * Updated changelog * Prepare 2.6.0 release * Address review comments, fix boundary issue * Cleanup tests * More tests and const empty list --- pkgs/async/CHANGELOG.md | 5 +- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/chunked_stream_reader.dart | 177 ++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/chunked_stream_reader.dart | 380 ++++++++++++++++++ 5 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 pkgs/async/lib/src/chunked_stream_reader.dart create mode 100644 pkgs/async/test/chunked_stream_reader.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index c7256ac1..84938d02 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,7 @@ -## 2.5.1-dev +## 2.6.0 + +* Added `ChunkedStreamReader` for reading _chunked streams_ without managing + buffers. ## 2.5.0 diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index a97fc65e..611d1376 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -37,3 +37,4 @@ export 'src/stream_subscription_transformer.dart'; export 'src/stream_zip.dart'; export 'src/subscription_stream.dart'; export 'src/typed_stream_transformer.dart'; +export 'src/chunked_stream_reader.dart'; diff --git a/pkgs/async/lib/src/chunked_stream_reader.dart b/pkgs/async/lib/src/chunked_stream_reader.dart new file mode 100644 index 00000000..855a7724 --- /dev/null +++ b/pkgs/async/lib/src/chunked_stream_reader.dart @@ -0,0 +1,177 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; +import 'dart:typed_data'; + +import 'byte_collector.dart' show collectBytes; + +/// Utility class for reading elements from a _chunked stream_. +/// +/// A _chunked stream_ is a stream where each event is a chunk of elements. +/// Byte-streams with the type `Stream>` is common of example of this. +/// As illustrated in the example below, this utility class makes it easy to +/// read a _chunked stream_ using custom chunk sizes and sub-stream sizes, +/// without managing partially read chunks. +/// +/// ```dart +/// final r = ChunkedStreamReader(File('myfile.txt').openRead()); +/// try { +/// // Read the first 4 bytes +/// final firstBytes = await r.readChunk(4); +/// if (firstBytes.length < 4) { +/// throw Exception('myfile.txt has less than 4 bytes'); +/// } +/// +/// // Read next 8 kilobytes as a substream +/// Stream> substream = r.readStream(8 * 1024); +/// +/// ... +/// } finally { +/// // We always cancel the ChunkedStreamReader, this ensures the underlying +/// // stream is cancelled. +/// r.cancel(); +/// } +/// ``` +/// +/// The read-operations [readChunk] and [readStream] must not be invoked until +/// the future from a previous call has completed. +class ChunkedStreamReader { + final StreamIterator> _input; + final List _emptyList = const []; + List _buffer = []; + bool _reading = false; + + factory ChunkedStreamReader(Stream> stream) => + ChunkedStreamReader._(StreamIterator(stream)); + + ChunkedStreamReader._(this._input); + + /// Read next [size] elements from _chunked stream_, buffering to create a + /// chunk with [size] elements. + /// + /// This will read _chunks_ from the underlying _chunked stream_ until [size] + /// elements have been buffered, or end-of-stream, then it returns the first + /// [size] buffered elements. + /// + /// If end-of-stream is encountered before [size] elements is read, this + /// returns a list with fewer than [size] elements (indicating end-of-stream). + /// + /// If the underlying stream throws, the stream is cancelled, the exception is + /// propogated and further read operations will fail. + /// + /// Throws, if another read operation is on-going. + Future> readChunk(int size) async { + final result = []; + await for (final chunk in readStream(size)) { + result.addAll(chunk); + } + return result; + } + + /// Read next [size] elements from _chunked stream_ as a sub-stream. + /// + /// This will pass-through _chunks_ from the underlying _chunked stream_ until + /// [size] elements have been returned, or end-of-stream has been encountered. + /// + /// If end-of-stream is encountered before [size] elements is read, this + /// returns a list with fewer than [size] elements (indicating end-of-stream). + /// + /// If the underlying stream throws, the stream is cancelled, the exception is + /// propogated and further read operations will fail. + /// + /// If the sub-stream returned from [readStream] is cancelled the remaining + /// unread elements up-to [size] are drained, allowing subsequent + /// read-operations to proceed after cancellation. + /// + /// Throws, if another read-operation is on-going. + Stream> readStream(int size) { + RangeError.checkNotNegative(size, 'size'); + if (_reading) { + throw StateError('Concurrent read operations are not allowed!'); + } + _reading = true; + + final substream = () async* { + // While we have data to read + while (size > 0) { + // Read something into the buffer, if it's empty + if (_buffer.isEmpty) { + if (!(await _input.moveNext())) { + // Don't attempt to read more data, as there is no more data. + size = 0; + _reading = false; + break; + } + _buffer = _input.current; + } + + if (_buffer.isNotEmpty) { + if (size < _buffer.length) { + final output = _buffer.sublist(0, size); + _buffer = _buffer.sublist(size); + size = 0; + yield output; + _reading = false; + break; + } + + final output = _buffer; + size -= _buffer.length; + _buffer = _emptyList; + yield output; + } + } + }; + + final c = StreamController>(); + c.onListen = () => c.addStream(substream()).whenComplete(c.close); + c.onCancel = () async { + while (size > 0) { + if (_buffer.isEmpty) { + if (!await _input.moveNext()) { + size = 0; // no more data + break; + } + _buffer = _input.current; + } + + if (size < _buffer.length) { + _buffer = _buffer.sublist(size); + size = 0; + break; + } + + size -= _buffer.length; + _buffer = _emptyList; + } + _reading = false; + }; + + return c.stream; + } + + /// Cancel the underlying _chunked stream_. + /// + /// If a future from [readChunk] or [readStream] is still pending then + /// [cancel] behaves as if the underlying stream ended early. That is a future + /// from [readChunk] may return a partial chunk smaller than the request size. + /// + /// It is always safe to call [cancel], even if the underlying stream was read + /// to completion. + /// + /// It can be a good idea to call [cancel] in a `finally`-block when done + /// using the [ChunkedStreamReader], this mitigates risk of leaking resources. + Future cancel() async => await _input.cancel(); +} + +/// Extensions for using [ChunkedStreamReader] with byte-streams. +extension ChunkedStreamReaderByteStreamExt on ChunkedStreamReader { + /// Read bytes into a [Uint8List]. + /// + /// This does the same as [readChunk], except it uses [collectBytes] to create + /// a [Uint8List], which offers better performance. + Future readBytes(int size) async => + await collectBytes(readStream(size)); +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e24947d5..5bfe9b13 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.5.1-dev +version: 2.6.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/chunked_stream_reader.dart b/pkgs/async/test/chunked_stream_reader.dart new file mode 100644 index 00000000..7dcd4083 --- /dev/null +++ b/pkgs/async/test/chunked_stream_reader.dart @@ -0,0 +1,380 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:test/test.dart'; +import 'package:async/async.dart'; + +void main() { + test('readChunk() chunk by chunk', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(2), equals([1, 2])); + expect(await r.readChunk(3), equals([3, 4, 5])); + expect(await r.readChunk(4), equals([6, 7, 8, 9])); + expect(await r.readChunk(1), equals([10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() element by element', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + for (var i = 0; i < 10; i++) { + expect(await r.readChunk(1), equals([i + 1])); + } + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() exact elements', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(10), equals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() past end', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(20), equals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() chunks of 2 elements', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(2), equals([1, 2])); + expect(await r.readChunk(2), equals([3, 4])); + expect(await r.readChunk(2), equals([5, 6])); + expect(await r.readChunk(2), equals([7, 8])); + expect(await r.readChunk(2), equals([9, 10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() chunks of 3 elements', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(3), equals([1, 2, 3])); + expect(await r.readChunk(3), equals([4, 5, 6])); + expect(await r.readChunk(3), equals([7, 8, 9])); + expect(await r.readChunk(3), equals([10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() cancel half way', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(5), equals([1, 2, 3, 4, 5])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() propagates exception', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + throw Exception('stopping here'); + }()); + + expect(await r.readChunk(3), equals([1, 2, 3])); + await expectLater(r.readChunk(3), throwsException); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readStream() forwards chunks', () async { + final chunk2 = [3, 4, 5]; + final chunk3 = [6, 7, 8, 9]; + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield chunk2; + yield chunk3; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + final i = StreamIterator(r.readStream(9)); + expect(await i.moveNext(), isTrue); + expect(i.current, equals([2])); + + // We must forward the exact chunks otherwise it's not efficient! + // Hence, we have a reference equality check here. + expect(await i.moveNext(), isTrue); + expect(i.current, equals([3, 4, 5])); + expect(i.current == chunk2, isTrue); + + expect(await i.moveNext(), isTrue); + expect(i.current, equals([6, 7, 8, 9])); + expect(i.current == chunk3, isTrue); + + expect(await i.moveNext(), isTrue); + expect(i.current, equals([10])); + expect(await i.moveNext(), isFalse); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readStream() cancel at the exact end', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + final i = StreamIterator(r.readStream(7)); + expect(await i.moveNext(), isTrue); + expect(i.current, equals([2])); + + expect(await i.moveNext(), isTrue); + expect(i.current, equals([3, 4, 5])); + + expect(await i.moveNext(), isTrue); + expect(i.current, equals([6, 7, 8])); + + await i.cancel(); // cancel substream just as it's ending + + expect(await r.readChunk(2), equals([9, 10])); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readStream() cancel at the exact end on chunk boundary', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + final i = StreamIterator(r.readStream(8)); + expect(await i.moveNext(), isTrue); + expect(i.current, equals([2])); + + expect(await i.moveNext(), isTrue); + expect(i.current, equals([3, 4, 5])); + + expect(await i.moveNext(), isTrue); + expect(i.current, equals([6, 7, 8, 9])); + + await i.cancel(); // cancel substream just as it's ending + + expect(await r.readChunk(2), equals([10])); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readStream() is drained when canceled', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + final i = StreamIterator(r.readStream(7)); + expect(await i.moveNext(), isTrue); + expect(i.current, equals([2])); + // Cancelling here should skip the remainder of the substream + // and we continue to read 9 and 10 from r + await i.cancel(); + + expect(await r.readChunk(2), equals([9, 10])); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readStream() concurrent reads is forbidden', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + // Notice we are not reading this substream: + r.readStream(7); + + expectLater(r.readChunk(2), throwsStateError); + }); + + test('readStream() supports draining', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + await r.readStream(7).drain(); + expect(await r.readChunk(2), equals([9, 10])); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('nested ChunkedStreamReader', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readChunk(1), equals([1])); + final r2 = ChunkedStreamReader(r.readStream(7)); + expect(await r2.readChunk(2), equals([2, 3])); + expect(await r2.readChunk(1), equals([4])); + await r2.cancel(); + + expect(await r.readChunk(2), equals([9, 10])); + + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readBytes() chunks of 3 elements', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2]; + yield [3, 4, 5]; + yield [6, 7, 8, 9]; + yield [10]; + }()); + + expect(await r.readBytes(3), allOf(equals([1, 2, 3]), isA())); + expect(await r.readBytes(3), allOf(equals([4, 5, 6]), isA())); + expect(await r.readBytes(3), allOf(equals([7, 8, 9]), isA())); + expect(await r.readBytes(3), allOf(equals([10]), isA())); + expect(await r.readBytes(1), equals([])); + expect(await r.readBytes(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readBytes(1), equals([])); + }); + + test('readChunk() until exact end of stream', () async { + final stream = Stream.fromIterable(Iterable.generate( + 10, + (_) => Uint8List(512), + )); + + final r = ChunkedStreamReader(stream); + while (true) { + final c = await r.readBytes(1024); + if (c.isEmpty) { + break; + } + } + }); + + test('cancel while readChunk() is pending', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2, 3]; + // This will hang forever, so we will call cancel() + await Completer().future; + yield [4]; // this should never be reachable + fail('unreachable!'); + }()); + + expect(await r.readBytes(2), equals([1, 2])); + + final future = r.readChunk(2); + + // Wait a tiny bit and cancel + await Future.microtask(() => null); + r.cancel(); + + expect(await future, hasLength(lessThan(2))); + }); + + test('cancel while readStream() is pending', () async { + final r = ChunkedStreamReader(() async* { + yield [1, 2, 3]; + // This will hang forever, so we will call cancel() + await Completer().future; + yield [4]; // this should never be reachable + fail('unreachable!'); + }()); + + expect(await collectBytes(r.readStream(2)), equals([1, 2])); + + final stream = r.readStream(2); + + // Wait a tiny bit and cancel + await Future.microtask(() => null); + r.cancel(); + + expect(await collectBytes(stream), hasLength(lessThan(2))); + }); +} From 842b7e8d7a7208a3da5766912903991a7224369b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 19 Apr 2021 18:09:55 -0700 Subject: [PATCH 182/260] Add isClosed getters to StreamGroup and FutureGroup (dart-lang/async#165) --- pkgs/async/CHANGELOG.md | 2 ++ pkgs/async/lib/src/future_group.dart | 4 +++- pkgs/async/lib/src/stream_group.dart | 2 ++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/future_group_test.dart | 4 ++++ pkgs/async/test/stream_group_test.dart | 2 ++ 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 84938d02..421db06a 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,8 @@ * Added `ChunkedStreamReader` for reading _chunked streams_ without managing buffers. +* Add `StreamGroup.isClosed` and `FutureGroup.isClosed` getters. + ## 2.5.0 * Stable release for null safety. diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 3a6291fd..1742d6ac 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -22,7 +22,9 @@ class FutureGroup implements Sink> { /// The number of futures that have yet to complete. var _pending = 0; - /// Whether [close] has been called. + /// Whether the group is closed, meaning that no more futures may be added. + bool get isClosed => _closed; + var _closed = false; /// The future that fires once [close] has been called and all futures in the diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 8c17ec20..c8649873 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -32,6 +32,8 @@ class StreamGroup implements Sink> { late StreamController _controller; /// Whether the group is closed, meaning that no more streams may be added. + bool get isClosed => _closed; + var _closed = false; /// The current state of the group. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 5bfe9b13..59ee5f21 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.6.0 +version: 2.6.0-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index 22e90f89..c2b1f3f0 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -26,7 +26,9 @@ void main() { test("completes once it's closed", () { expect(futureGroup.future, completion(isEmpty)); + expect(futureGroup.isClosed, isFalse); futureGroup.close(); + expect(futureGroup.isClosed, isTrue); }); }); @@ -47,7 +49,9 @@ void main() { await flushMicrotasks(); expect(futureGroup.future, completes); + expect(futureGroup.isClosed, isFalse); futureGroup.close(); + expect(futureGroup.isClosed, isTrue); }); test("completes to that future's value", () { diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index eadae19a..43f9bfca 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -543,7 +543,9 @@ void regardlessOfType(StreamGroup Function() newStreamGroup) { streamGroup.add(controller2.stream); await flushMicrotasks(); + expect(streamGroup.isClosed, isFalse); streamGroup.close(); + expect(streamGroup.isClosed, isTrue); streamGroup.remove(controller1.stream); await flushMicrotasks(); From dc46ed091c0a64eb88e671f232ffa74a7427a24c Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 21 Apr 2021 08:50:35 -0700 Subject: [PATCH 183/260] Style tweaks in stream_queue.dart (dart-lang/async#167) - Use a noun phrase to document the `hasNext` getter. The `next` and `peek` getters keep their verb phrase doc comments since the side effects are critical distinctions between these methods. If we were writing this today, `next()` would be a method. - Change `_failClosed()` which returns an error, to `_checkNotClosed()` which checks the condition and optionally closes. Avoid nesting the majority of method behavior in a conditional. - Use `RangeError.checkNotNegative` over a conditional. - Change bare `Future` to `Future` in code examples. - Make `withTransaction` `async` since there is no longer a blocker now that async methods start running synchronously. --- pkgs/async/lib/src/stream_queue.dart | 120 ++++++++++++--------------- 1 file changed, 52 insertions(+), 68 deletions(-) diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index 5aa60548..f9902b96 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -126,7 +126,7 @@ class StreamQueue { } } - /// Asks if the stream has any more events. + /// Whether the stream has any more events. /// /// Returns a future that completes with `true` if the stream has any /// more events, whether data or error. @@ -138,12 +138,10 @@ class StreamQueue { /// Another alternative is to use `take(1)` which returns either zero or /// one events. Future get hasNext { - if (!_isClosed) { - var hasNextRequest = _HasNextRequest(); - _addRequest(hasNextRequest); - return hasNextRequest.future; - } - throw _failClosed(); + _checkNotClosed(); + var hasNextRequest = _HasNextRequest(); + _addRequest(hasNextRequest); + return hasNextRequest.future; } /// Look at the next [count] data events without consuming them. @@ -152,13 +150,11 @@ class StreamQueue { /// If one of the next [count] events is an error, the returned future /// completes with this error, and the error is still left in the queue. Future> lookAhead(int count) { - if (count < 0) throw RangeError.range(count, 0, null, 'count'); - if (!_isClosed) { - var request = _LookAheadRequest(count); - _addRequest(request); - return request.future; - } - throw _failClosed(); + RangeError.checkNotNegative(count, 'count'); + _checkNotClosed(); + var request = _LookAheadRequest(count); + _addRequest(request); + return request.future; } /// Requests the next (yet unrequested) event from the stream. @@ -176,12 +172,10 @@ class StreamQueue { /// and they will be completed in the order they were requested, by the /// first events that were not consumed by previous requeusts. Future get next { - if (!_isClosed) { - var nextRequest = _NextRequest(); - _addRequest(nextRequest); - return nextRequest.future; - } - throw _failClosed(); + _checkNotClosed(); + var nextRequest = _NextRequest(); + _addRequest(nextRequest); + return nextRequest.future; } /// Looks at the next (yet unrequested) event from the stream. @@ -189,15 +183,13 @@ class StreamQueue { /// Like [next] except that the event is not consumed. /// If the next event is an error event, it stays in the queue. Future get peek { - if (!_isClosed) { - var nextRequest = _PeekRequest(); - _addRequest(nextRequest); - return nextRequest.future; - } - throw _failClosed(); + _checkNotClosed(); + var nextRequest = _PeekRequest(); + _addRequest(nextRequest); + return nextRequest.future; } - /// Returns a stream of all the remaning events of the source stream. + /// A stream of all the remaning events of the source stream. /// /// All requested [next], [skip] or [take] operations are completed /// first, and then any remaining events are provided as events of @@ -207,9 +199,7 @@ class StreamQueue { /// `rest` the caller may no longer request other events, like /// after calling [cancel]. Stream get rest { - if (_isClosed) { - throw _failClosed(); - } + _checkNotClosed(); var request = _RestRequest(this); _isClosed = true; _addRequest(request); @@ -232,13 +222,11 @@ class StreamQueue { /// then all events were succssfully skipped. If the value /// is greater than zero then the stream ended early. Future skip(int count) { - if (count < 0) throw RangeError.range(count, 0, null, 'count'); - if (!_isClosed) { - var request = _SkipRequest(count); - _addRequest(request); - return request.future; - } - throw _failClosed(); + RangeError.checkNotNegative(count, 'count'); + _checkNotClosed(); + var request = _SkipRequest(count); + _addRequest(request); + return request.future; } /// Requests the next [count] data events as a list. @@ -257,13 +245,11 @@ class StreamQueue { /// of data collected so far. That is, the returned /// list may have fewer than [count] elements. Future> take(int count) { - if (count < 0) throw RangeError.range(count, 0, null, 'count'); - if (!_isClosed) { - var request = _TakeRequest(count); - _addRequest(request); - return request.future; - } - throw _failClosed(); + RangeError.checkNotNegative(count, 'count'); + _checkNotClosed(); + var request = _TakeRequest(count); + _addRequest(request); + return request.future; } /// Requests a transaction that can conditionally consume events. @@ -285,7 +271,7 @@ class StreamQueue { /// /// ```dart /// /// Consumes all empty lines from the beginning of [lines]. - /// Future consumeEmptyLines(StreamQueue lines) async { + /// Future consumeEmptyLines(StreamQueue lines) async { /// while (await lines.hasNext) { /// var transaction = lines.startTransaction(); /// var queue = transaction.newQueue(); @@ -299,7 +285,7 @@ class StreamQueue { /// } /// ``` StreamQueueTransaction startTransaction() { - if (_isClosed) throw _failClosed(); + _checkNotClosed(); var request = _TransactionRequest(this); _addRequest(request); @@ -320,7 +306,7 @@ class StreamQueue { /// /// ```dart /// /// Consumes all empty lines from the beginning of [lines]. - /// Future consumeEmptyLines(StreamQueue lines) async { + /// Future consumeEmptyLines(StreamQueue lines) async { /// while (await lines.hasNext) { /// // Consume a line if it's empty, otherwise return. /// if (!await lines.withTransaction( @@ -330,23 +316,24 @@ class StreamQueue { /// } /// } /// ``` - Future withTransaction(Future Function(StreamQueue) callback) { + Future withTransaction( + Future Function(StreamQueue) callback) async { var transaction = startTransaction(); - /// Avoid async/await to ensure that [startTransaction] is called - /// synchronously and so ends up in the right place in the request queue. var queue = transaction.newQueue(); - return callback(queue).then((result) { - if (result) { - transaction.commit(queue); - } else { - transaction.reject(); - } - return result; - }, onError: (Object error) { + bool result; + try { + result = await callback(queue); + } catch (_) { transaction.commit(queue); - throw error; - }); + rethrow; + } + if (result) { + transaction.commit(queue); + } else { + transaction.reject(); + } + return result; } /// Passes a copy of this queue to [callback], and updates this queue to match @@ -394,13 +381,13 @@ class StreamQueue { /// stream had closed. /// /// The returned future completes with the result of calling - /// `cancel`. + /// `cancel` on the subscription to the source stream. /// /// After calling `cancel`, no further events can be requested. /// None of [lookAhead], [next], [peek], [rest], [skip], [take] or [cancel] /// may be called again. Future? cancel({bool immediate = false}) { - if (_isClosed) throw _failClosed(); + _checkNotClosed(); _isClosed = true; if (!immediate) { @@ -529,12 +516,9 @@ class StreamQueue { // ------------------------------------------------------------------ // Internal helper methods. - /// Returns an error for when a request is made after cancel. - /// - /// Returns a [StateError] with a message saying that either - /// [cancel] or [rest] have already been called. - Error _failClosed() { - return StateError('Already cancelled'); + /// Throws an error if [cancel] or [rest] have already been called. + void _checkNotClosed() { + if (_isClosed) throw StateError('Already cancelled'); } /// Adds a new request to the queue. From 9f3f170a08d028c2d35e06b6d39ecb2fccaa04a0 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 27 Apr 2021 15:07:52 -0700 Subject: [PATCH 184/260] Add StreamGroup.onIdle and StreamGroup.isIdle (dart-lang/async#164) These match the corresponding APIs on FutureGroup. --- pkgs/async/CHANGELOG.md | 2 + pkgs/async/lib/src/future_group.dart | 24 ++++-- pkgs/async/lib/src/stream_group.dart | 50 +++++++++++- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_group_test.dart | 103 +++++++++++++++++++++++++ 5 files changed, 174 insertions(+), 7 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 421db06a..0b23130c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,8 @@ * Added `ChunkedStreamReader` for reading _chunked streams_ without managing buffers. +* Add `StreamGroup.isIdle` and `StreamGroup.onIdle`. + * Add `StreamGroup.isClosed` and `FutureGroup.isClosed` getters. ## 2.5.0 diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index 1742d6ac..c98fbc3c 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -35,14 +35,28 @@ class FutureGroup implements Sink> { Future> get future => _completer.future; final _completer = Completer>(); - /// Whether this group has no pending futures. + /// Whether this group contains no futures. + /// + /// A [FutureGroup] is idle when it contains no futures, which is the case for + /// a newly created group or one where all added futures have been removed or + /// completed. bool get isIdle => _pending == 0; - /// A broadcast stream that emits a `null` event whenever the last pending - /// future in this group completes. + /// A broadcast stream that emits an event whenever this group becomes idle. + /// + /// A [FutureGroup] is idle when it contains no futures, which is the case for + /// a newly created group or one where all added futures have been removed or + /// completed. + /// + /// This stream will close when this group is idle *and* [close] has been + /// called. + /// + /// Note that: /// - /// Once this group isn't waiting on any futures *and* [close] has been - /// called, this stream will close. + /// * Events won't be emitted on this stream until [stream] has been listened + /// to. + /// * Events are delivered asynchronously, so it's possible for the group to + /// become active again before the event is delivered. Stream get onIdle => (_onIdleController ??= StreamController.broadcast(sync: true)).stream; diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index c8649873..49baaad1 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -41,6 +41,39 @@ class StreamGroup implements Sink> { /// See [_StreamGroupState] for detailed descriptions of each state. var _state = _StreamGroupState.dormant; + /// Whether this group contains no streams. + /// + /// A [StreamGroup] is idle when it contains no streams, which is the case for + /// a newly created group or one where all added streams have been emitted + /// done events (or been [remove]d). + /// + /// If this is a single-subscription group, then cancelling the subscription + /// to [stream] will also remove all streams. + bool get isIdle => _subscriptions.isEmpty; + + /// A broadcast stream that emits an event whenever this group becomes idle. + /// + /// A [StreamGroup] is idle when it contains no streams, which is the case for + /// a newly created group or one where all added streams have been emitted + /// done events (or been [remove]d). + /// + /// This stream will close when either: + /// + /// * This group is idle *and* [close] has been called, or + /// * [stream]'s subscription has been cancelled (if this is a + /// single-subscription group). + /// + /// Note that: + /// + /// * Events won't be emitted on this stream until [stream] has been listened + /// to. + /// * Events are delivered asynchronously, so it's possible for the group to + /// become active again before the event is delivered. + Stream get onIdle => + (_onIdleController ??= StreamController.broadcast()).stream; + + StreamController? _onIdleController; + /// Streams that have been added to the group, and their subscriptions if they /// have been subscribed to. /// @@ -135,7 +168,15 @@ class StreamGroup implements Sink> { Future? remove(Stream stream) { var subscription = _subscriptions.remove(stream); var future = subscription == null ? null : subscription.cancel(); - if (_closed && _subscriptions.isEmpty) _controller.close(); + + if (_subscriptions.isEmpty) { + _onIdleController?.add(null); + if (_closed) { + _onIdleController?.close(); + scheduleMicrotask(_controller.close); + } + } + return future; } @@ -180,6 +221,13 @@ class StreamGroup implements Sink> { .toList(); _subscriptions.clear(); + + var onIdleController = _onIdleController; + if (onIdleController != null && !onIdleController.isClosed) { + onIdleController.add(null); + onIdleController.close(); + } + return futures.isEmpty ? null : Future.wait(futures); } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 59ee5f21..5bfe9b13 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.6.0-dev +version: 2.6.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 43f9bfca..7ea93f8c 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -736,6 +736,109 @@ void regardlessOfType(StreamGroup Function() newStreamGroup) { expect(events, equals(['one', 'two', 'three', 'four', 'five', 'six'])); }); }); + + group('onIdle', () { + test('emits an event when the last pending stream emits done', () async { + streamGroup.stream.listen(null); + + var idle = false; + streamGroup.onIdle.listen((_) => idle = true); + + var controller1 = StreamController(); + var controller2 = StreamController(); + var controller3 = StreamController(); + + streamGroup.add(controller1.stream); + streamGroup.add(controller2.stream); + streamGroup.add(controller3.stream); + + await flushMicrotasks(); + expect(idle, isFalse); + expect(streamGroup.isIdle, isFalse); + + controller1.close(); + await flushMicrotasks(); + expect(idle, isFalse); + expect(streamGroup.isIdle, isFalse); + + controller2.close(); + await flushMicrotasks(); + expect(idle, isFalse); + expect(streamGroup.isIdle, isFalse); + + controller3.close(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(streamGroup.isIdle, isTrue); + }); + + test('emits an event each time it becomes idle', () async { + streamGroup.stream.listen(null); + + var idle = false; + streamGroup.onIdle.listen((_) => idle = true); + + var controller = StreamController(); + streamGroup.add(controller.stream); + + controller.close(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(streamGroup.isIdle, isTrue); + + idle = false; + controller = StreamController(); + streamGroup.add(controller.stream); + + await flushMicrotasks(); + expect(idle, isFalse); + expect(streamGroup.isIdle, isFalse); + + controller.close(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(streamGroup.isIdle, isTrue); + }); + + test('emits an event when the group closes', () async { + // It's important that the order of events here stays consistent over + // time, since code may rely on it in subtle ways. Note that this is *not* + // an official guarantee, so the authors of `async` are free to change + // this behavior if they need to. + var idle = false; + var onIdleDone = false; + var streamClosed = false; + + streamGroup.onIdle.listen(expectAsync1((_) { + expect(streamClosed, isFalse); + idle = true; + }), onDone: expectAsync0(() { + expect(idle, isTrue); + expect(streamClosed, isTrue); + onIdleDone = true; + })); + + streamGroup.stream.drain().then(expectAsync1((_) { + expect(idle, isTrue); + expect(onIdleDone, isFalse); + streamClosed = true; + })); + + var controller = StreamController(); + streamGroup.add(controller.stream); + streamGroup.close(); + + await flushMicrotasks(); + expect(idle, isFalse); + expect(streamGroup.isIdle, isFalse); + + controller.close(); + await flushMicrotasks(); + expect(idle, isTrue); + expect(streamGroup.isIdle, isTrue); + expect(streamClosed, isTrue); + }); + }); } /// Wait for all microtasks to complete. From 7d44918a322192e01c57c255e052ea4008b595d7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 27 Apr 2021 15:08:38 -0700 Subject: [PATCH 185/260] Add a StreamCloser transformer (dart-lang/async#166) This is essentially the Stream equivalent of the Disconnector class in stream_channel. --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/stream_closer.dart | 108 ++++++++++++ pkgs/async/pubspec.yaml | 1 + pkgs/async/test/stream_closer_test.dart | 208 ++++++++++++++++++++++++ 5 files changed, 321 insertions(+) create mode 100644 pkgs/async/lib/src/stream_closer.dart create mode 100644 pkgs/async/test/stream_closer_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 0b23130c..a0f15c11 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,8 @@ ## 2.6.0 +* Add a `StreamCloser` class, which is a `StreamTransformer` that allows the + caller to force the stream to emit a done event. + * Added `ChunkedStreamReader` for reading _chunked streams_ without managing buffers. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 611d1376..7b9d9426 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -27,6 +27,7 @@ export 'src/result/error.dart'; export 'src/result/future.dart'; export 'src/result/value.dart'; export 'src/single_subscription_transformer.dart'; +export 'src/stream_closer.dart'; export 'src/stream_completer.dart'; export 'src/stream_group.dart'; export 'src/stream_queue.dart'; diff --git a/pkgs/async/lib/src/stream_closer.dart b/pkgs/async/lib/src/stream_closer.dart new file mode 100644 index 00000000..91546242 --- /dev/null +++ b/pkgs/async/lib/src/stream_closer.dart @@ -0,0 +1,108 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; + +import 'package:meta/meta.dart'; + +/// A [StreamTransformer] that allows the caller to forcibly close the +/// transformed [Stream](s). +/// +/// When [close] is called, any stream (or streams) transformed by this +/// transformer that haven't already completed or been cancelled will emit a +/// done event and cancel their underlying subscriptions. +/// +/// Note that unlike most [StreamTransformer]s, each instance of [StreamCloser] +/// has its own state (whether or not it's been closed), so it's a good idea to +/// construct a new one for each use unless you need to close multiple streams +/// at the same time. +@sealed +class StreamCloser extends StreamTransformerBase { + /// The subscriptions to streams passed to [bind]. + final _subscriptions = >{}; + + /// The controllers for streams returned by [bind]. + final _controllers = >{}; + + /// Closes all transformed streams. + /// + /// Returns a future that completes when all inner subscriptions' + /// [StreamSubscription.cancel] futures have completed. Note that a stream's + /// subscription won't be canceled until the transformed stream has a + /// listener. + /// + /// If a transformed stream is listened to after [close] is called, the + /// original stream will be listened to and then the subscription immediately + /// canceled. If that cancellation throws an error, it will be silently + /// ignored. + Future close() => _closeFuture ??= () { + var futures = [ + for (var subscription in _subscriptions) subscription.cancel() + ]; + _subscriptions.clear(); + + var controllers = _controllers.toList(); + _controllers.clear(); + scheduleMicrotask(() { + for (var controller in controllers) { + scheduleMicrotask(controller.close); + } + }); + + return Future.wait(futures, eagerError: true); + }(); + Future? _closeFuture; + + /// Whether [close] has been called. + bool get isClosed => _closeFuture != null; + + @override + Stream bind(Stream stream) { + var controller = stream.isBroadcast + ? StreamController.broadcast(sync: true) + : StreamController(sync: true); + + controller.onListen = () { + if (isClosed) { + // Ignore errors here, because otherwise there would be no way for the + // user to handle them gracefully. + stream.listen(null).cancel().catchError((_) {}); + return; + } + + var subscription = + stream.listen(controller.add, onError: controller.addError); + subscription.onDone(() { + _subscriptions.remove(subscription); + _controllers.remove(controller); + controller.close(); + }); + _subscriptions.add(subscription); + + if (!stream.isBroadcast) { + controller.onPause = subscription.pause; + controller.onResume = subscription.resume; + } + + controller.onCancel = () { + _controllers.remove(controller); + + // If the subscription has already been removed, that indicates that the + // underlying stream has been cancelled by [close] and its cancellation + // future has been handled there. In that case, we shouldn't forward it + // here as well. + if (_subscriptions.remove(subscription)) return subscription.cancel(); + return null; + }; + }; + + if (isClosed) { + controller.close(); + } else { + _controllers.add(controller); + } + + return controller.stream; + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 5bfe9b13..d645c472 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -8,6 +8,7 @@ environment: sdk: ">=2.12.0 <3.0.0" dependencies: + meta: ^1.1.7 collection: ^1.15.0 dev_dependencies: diff --git a/pkgs/async/test/stream_closer_test.dart b/pkgs/async/test/stream_closer_test.dart new file mode 100644 index 00000000..28ab9703 --- /dev/null +++ b/pkgs/async/test/stream_closer_test.dart @@ -0,0 +1,208 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +import 'utils.dart'; + +void main() { + late StreamCloser closer; + setUp(() { + closer = StreamCloser(); + }); + + group('when the closer is never closed', () { + test('forwards data and done events', () { + expect( + createStream().transform(closer).toList(), completion([1, 2, 3, 4])); + }); + + test('forwards error events', () { + expect(Stream.error('oh no').transform(closer).toList(), + throwsA('oh no')); + }); + + test('transforms a broadcast stream into a broadcast stream', () { + expect(Stream.empty().transform(closer).isBroadcast, isTrue); + }); + + test("doesn't eagerly listen", () { + var controller = StreamController(); + var transformed = controller.stream.transform(closer); + expect(controller.hasListener, isFalse); + + transformed.listen(null); + expect(controller.hasListener, isTrue); + }); + + test('forwards pause and resume', () { + var controller = StreamController(); + var transformed = controller.stream.transform(closer); + + var subscription = transformed.listen(null); + expect(controller.isPaused, isFalse); + subscription.pause(); + expect(controller.isPaused, isTrue); + subscription.resume(); + expect(controller.isPaused, isFalse); + }); + + test('forwards cancel', () { + var isCancelled = false; + var controller = + StreamController(onCancel: () => isCancelled = true); + var transformed = controller.stream.transform(closer); + + expect(isCancelled, isFalse); + var subscription = transformed.listen(null); + expect(isCancelled, isFalse); + subscription.cancel(); + expect(isCancelled, isTrue); + }); + + test('forwards errors from cancel', () { + var controller = StreamController(onCancel: () => throw 'oh no'); + + expect(controller.stream.transform(closer).listen(null).cancel(), + throwsA('oh no')); + }); + }); + + group('when a stream is added before the closer is closed', () { + test('the stream emits a close event once the closer is closed', () async { + var queue = StreamQueue(createStream().transform(closer)); + await expectLater(queue, emits(1)); + await expectLater(queue, emits(2)); + expect(closer.close(), completes); + expect(queue, emitsDone); + }); + + test('the inner subscription is canceled once the closer is closed', () { + var isCancelled = false; + var controller = + StreamController(onCancel: () => isCancelled = true); + + expect(controller.stream.transform(closer), emitsDone); + expect(closer.close(), completes); + expect(isCancelled, isTrue); + }); + + test('closer.close() forwards errors from StreamSubscription.cancel()', () { + var controller = StreamController(onCancel: () => throw 'oh no'); + + expect(controller.stream.transform(closer), emitsDone); + expect(closer.close(), throwsA('oh no')); + }); + + test('closer.close() works even if a stream has already completed', + () async { + expect(await createStream().transform(closer).toList(), + equals([1, 2, 3, 4])); + expect(closer.close(), completes); + }); + + test('closer.close() works even if a stream has already been canceled', + () async { + createStream().transform(closer).listen(null).cancel(); + expect(closer.close(), completes); + }); + + group('but listened afterwards', () { + test('the output stream immediately emits done', () { + var stream = createStream().transform(closer); + expect(closer.close(), completes); + expect(stream, emitsDone); + }); + + test( + 'the underlying subscription is never listened if the stream is ' + 'never listened', () async { + var controller = + StreamController(onListen: expectAsync0(() {}, count: 0)); + controller.stream.transform(closer); + + expect(closer.close(), completes); + + await pumpEventQueue(); + }); + + test( + 'the underlying subscription is listened and then canceled once the ' + 'stream is listened', () { + var controller = StreamController( + onListen: expectAsync0(() {}), onCancel: expectAsync0(() {})); + var stream = controller.stream.transform(closer); + + expect(closer.close(), completes); + + stream.listen(null); + }); + + test('Subscription.cancel() errors are silently ignored', () async { + var controller = + StreamController(onCancel: expectAsync0(() => throw 'oh no')); + var stream = controller.stream.transform(closer); + + expect(closer.close(), completes); + + stream.listen(null); + await pumpEventQueue(); + }); + }); + }); + + group('when a stream is added after the closer is closed', () { + test('the output stream immediately emits done', () { + expect(closer.close(), completes); + expect(createStream().transform(closer), emitsDone); + }); + + test( + 'the underlying subscription is never listened if the stream is never ' + 'listened', () async { + expect(closer.close(), completes); + + var controller = + StreamController(onListen: expectAsync0(() {}, count: 0)); + controller.stream.transform(closer); + + await pumpEventQueue(); + }); + + test( + 'the underlying subscription is listened and then canceled once the ' + 'stream is listened', () { + expect(closer.close(), completes); + + var controller = StreamController( + onListen: expectAsync0(() {}), onCancel: expectAsync0(() {})); + + controller.stream.transform(closer).listen(null); + }); + + test('Subscription.cancel() errors are silently ignored', () async { + expect(closer.close(), completes); + + var controller = + StreamController(onCancel: expectAsync0(() => throw 'oh no')); + + controller.stream.transform(closer).listen(null); + + await pumpEventQueue(); + }); + }); +} + +Stream createStream() async* { + yield 1; + await flushMicrotasks(); + yield 2; + await flushMicrotasks(); + yield 3; + await flushMicrotasks(); + yield 4; +} From b5eeda444e1b30d65374b45f898c14c2e15f5584 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 27 Apr 2021 15:27:36 -0700 Subject: [PATCH 186/260] Add a StreamSink.rejectErrors() extension method (dart-lang/async#169) This makes it easy for authors to expose sinks that can't natively consume errors, but still handle them in a consistent and robust manner. --- pkgs/async/CHANGELOG.md | 3 + pkgs/async/lib/async.dart | 1 + .../async/lib/src/stream_sink_extensions.dart | 22 ++ .../reject_errors.dart | 127 +++++++++++ pkgs/async/test/reject_errors_test.dart | 205 ++++++++++++++++++ 5 files changed, 358 insertions(+) create mode 100644 pkgs/async/lib/src/stream_sink_extensions.dart create mode 100644 pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart create mode 100644 pkgs/async/test/reject_errors_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index a0f15c11..e26b7122 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -6,6 +6,9 @@ * Added `ChunkedStreamReader` for reading _chunked streams_ without managing buffers. +* Add extensions on `StreamSink`, including `StreamSink.transform()` for + applying `StreamSinkTransformer`s and `StreamSink.rejectErrors()`. + * Add `StreamGroup.isIdle` and `StreamGroup.onIdle`. * Add `StreamGroup.isClosed` and `FutureGroup.isClosed` getters. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 7b9d9426..2d5876a0 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -32,6 +32,7 @@ export 'src/stream_completer.dart'; export 'src/stream_group.dart'; export 'src/stream_queue.dart'; export 'src/stream_sink_completer.dart'; +export 'src/stream_sink_extensions.dart'; export 'src/stream_sink_transformer.dart'; export 'src/stream_splitter.dart'; export 'src/stream_subscription_transformer.dart'; diff --git a/pkgs/async/lib/src/stream_sink_extensions.dart b/pkgs/async/lib/src/stream_sink_extensions.dart new file mode 100644 index 00000000..ed43341c --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_extensions.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; + +import 'stream_sink_transformer.dart'; +import 'stream_sink_transformer/reject_errors.dart'; + +/// Extensions on [StreamSink] to make stream transformations more fluent. +extension StreamSinkExtensions on StreamSink { + /// Transforms a [StreamSink] using [transformer]. + StreamSink transform(StreamSinkTransformer transformer) => + transformer.bind(this); + + /// Returns a [StreamSink] that forwards to [this] but rejects errors. + /// + /// If an error is passed (either by [addError] or [addStream]), the + /// underlying sink will be closed and the error will be forwarded to the + /// returned sink's [StreamSink.done] future. Further events will be ignored. + StreamSink rejectErrors() => RejectErrorsSink(this); +} diff --git a/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart b/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart new file mode 100644 index 00000000..a8d130f8 --- /dev/null +++ b/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart @@ -0,0 +1,127 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; + +/// A [StreamSink] wrapper that rejects all errors passed into the sink. +class RejectErrorsSink implements StreamSink { + /// The target sink. + final StreamSink _inner; + + @override + Future get done => _doneCompleter.future; + final _doneCompleter = Completer(); + + /// Whether the user has called [close]. + /// + /// If [_closed] is true, [_canceled] must be true and [_inAddStream] must be + /// false. + bool _closed = false; + + /// The subscription to the stream passed to [addStream], if a stream is + /// currently being added. + StreamSubscription? _addStreamSubscription; + + /// The completer for the future returned by [addStream], if a stream is + /// currently being added. + Completer? _addStreamCompleter; + + /// Whether we're currently adding a stream with [addStream]. + bool get _inAddStream => _addStreamSubscription != null; + + RejectErrorsSink(this._inner) { + _inner.done.then((value) { + _cancelAddStream(); + if (!_canceled) _doneCompleter.complete(value); + }).onError((error, stackTrace) { + _cancelAddStream(); + if (!_canceled) _doneCompleter.completeError(error, stackTrace); + }); + } + + /// Whether the underlying sink is no longer receiving events. + /// + /// This can happen if: + /// + /// * [close] has been called, + /// * an error has been passed, + /// * or the underlying [StreamSink.done] has completed. + /// + /// If [_canceled] is true, [_inAddStream] must be false. + bool get _canceled => _doneCompleter.isCompleted; + + @override + void add(T data) { + if (_closed) throw StateError('Cannot add event after closing.'); + if (_inAddStream) { + throw StateError('Cannot add event while adding stream.'); + } + if (_canceled) return; + + _inner.add(data); + } + + @override + void addError(error, [StackTrace? stackTrace]) { + if (_closed) throw StateError('Cannot add event after closing.'); + if (_inAddStream) { + throw StateError('Cannot add event while adding stream.'); + } + if (_canceled) return; + + _addError(error, stackTrace); + } + + /// Like [addError], but doesn't check to ensure that an error can be added. + /// + /// This is called from [addStream], so it shouldn't fail if a stream is being + /// added. + void _addError(Object error, [StackTrace? stackTrace]) { + _cancelAddStream(); + _doneCompleter.completeError(error, stackTrace); + + // Ignore errors from the inner sink. We're already surfacing one error, and + // if the user handles it we don't want them to have another top-level. + _inner.close().catchError((_) {}); + } + + @override + Future addStream(Stream stream) { + if (_closed) throw StateError('Cannot add stream after closing.'); + if (_inAddStream) { + throw StateError('Cannot add stream while adding stream.'); + } + if (_canceled) return Future.value(); + + var addStreamCompleter = _addStreamCompleter = Completer.sync(); + _addStreamSubscription = stream.listen(_inner.add, + onError: _addError, onDone: addStreamCompleter.complete); + return addStreamCompleter.future.then((_) { + _addStreamCompleter = null; + _addStreamSubscription = null; + }); + } + + @override + Future close() { + if (_inAddStream) { + throw StateError('Cannot close sink while adding stream.'); + } + + if (_closed) return done; + _closed = true; + + if (!_canceled) _doneCompleter.complete(_inner.close()); + return done; + } + + /// If an [addStream] call is active, cancel its subscription and complete its + /// completer. + void _cancelAddStream() { + if (!_inAddStream) return; + _addStreamCompleter!.complete(_addStreamSubscription!.cancel()); + _addStreamCompleter = null; + _addStreamSubscription = null; + } +} diff --git a/pkgs/async/test/reject_errors_test.dart b/pkgs/async/test/reject_errors_test.dart new file mode 100644 index 00000000..32bffd15 --- /dev/null +++ b/pkgs/async/test/reject_errors_test.dart @@ -0,0 +1,205 @@ +// Copyright (c) 2021, 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 filevents. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +void main() { + late StreamController controller; + setUp(() { + controller = StreamController(); + }); + + test('passes through data events', () { + controller.sink.rejectErrors()..add(1)..add(2)..add(3); + expect(controller.stream, emitsInOrder([1, 2, 3])); + }); + + test('passes through close events', () { + controller.sink.rejectErrors() + ..add(1) + ..close(); + expect(controller.stream, emitsInOrder([1, emitsDone])); + }); + + test('passes through data events from addStream()', () { + controller.sink.rejectErrors().addStream(Stream.fromIterable([1, 2, 3])); + expect(controller.stream, emitsInOrder([1, 2, 3])); + }); + + test('allows multiple addStream() calls', () async { + var transformed = controller.sink.rejectErrors(); + await transformed.addStream(Stream.fromIterable([1, 2, 3])); + await transformed.addStream(Stream.fromIterable([4, 5, 6])); + expect(controller.stream, emitsInOrder([1, 2, 3, 4, 5, 6])); + }); + + group('on addError()', () { + test('forwards the error to done', () { + var transformed = controller.sink.rejectErrors(); + transformed.addError('oh no'); + expect(transformed.done, throwsA('oh no')); + }); + + test('closes the underlying sink', () { + var transformed = controller.sink.rejectErrors(); + transformed.addError('oh no'); + transformed.done.catchError((_) {}); + + expect(controller.stream, emitsDone); + }); + + test('ignores further events', () async { + var transformed = controller.sink.rejectErrors(); + transformed.addError('oh no'); + transformed.done.catchError((_) {}); + expect(controller.stream, emitsDone); + + // Try adding events synchronously and asynchronously and verify that they + // don't throw and also aren't passed to the underlying sink. + transformed + ..add(1) + ..addError('another'); + await pumpEventQueue(); + transformed + ..add(2) + ..addError('yet another'); + }); + + test('cancels the current subscription', () async { + var inputCanceled = false; + var inputController = + StreamController(onCancel: () => inputCanceled = true); + + var transformed = controller.sink.rejectErrors() + ..addStream(inputController.stream); + inputController.addError('oh no'); + transformed.done.catchError((_) {}); + + await pumpEventQueue(); + expect(inputCanceled, isTrue); + }); + }); + + group('when the inner sink\'s done future completes', () { + test('done completes', () async { + var completer = Completer(); + var transformed = NullStreamSink(done: completer.future).rejectErrors(); + + var doneCompleted = false; + transformed.done.then((_) => doneCompleted = true); + await pumpEventQueue(); + expect(doneCompleted, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(doneCompleted, isTrue); + }); + + test('an outstanding addStream() completes', () async { + var completer = Completer(); + var transformed = NullStreamSink(done: completer.future).rejectErrors(); + + var addStreamCompleted = false; + transformed + .addStream(StreamController().stream) + .then((_) => addStreamCompleted = true); + await pumpEventQueue(); + expect(addStreamCompleted, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(addStreamCompleted, isTrue); + }); + + test('an outstanding addStream()\'s subscription is cancelled', () async { + var completer = Completer(); + var transformed = NullStreamSink(done: completer.future).rejectErrors(); + + var addStreamCancelled = false; + transformed.addStream( + StreamController(onCancel: () => addStreamCancelled = true).stream); + await pumpEventQueue(); + expect(addStreamCancelled, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(addStreamCancelled, isTrue); + }); + + test('forwards an outstanding addStream()\'s cancellation error', () async { + var completer = Completer(); + var transformed = NullStreamSink(done: completer.future).rejectErrors(); + + expect( + transformed.addStream( + StreamController(onCancel: () => throw 'oh no').stream), + throwsA('oh no')); + completer.complete(); + }); + + group('forwards its error', () { + test('through done', () async { + expect(NullStreamSink(done: Future.error('oh no')).rejectErrors().done, + throwsA('oh no')); + }); + + test('through close', () async { + expect( + NullStreamSink(done: Future.error('oh no')).rejectErrors().close(), + throwsA('oh no')); + }); + }); + }); + + group('after closing', () { + test('throws on add()', () { + var sink = controller.sink.rejectErrors()..close(); + expect(() => sink.add(1), throwsStateError); + }); + + test('throws on addError()', () { + var sink = controller.sink.rejectErrors()..close(); + expect(() => sink.addError('oh no'), throwsStateError); + }); + + test('throws on addStream()', () { + var sink = controller.sink.rejectErrors()..close(); + expect(() => sink.addStream(Stream.empty()), throwsStateError); + }); + + test('allows close()', () { + var sink = controller.sink.rejectErrors()..close(); + sink.close(); // Shouldn't throw + }); + }); + + group('during an active addStream()', () { + test('throws on add()', () { + var sink = controller.sink.rejectErrors() + ..addStream(StreamController().stream); + expect(() => sink.add(1), throwsStateError); + }); + + test('throws on addError()', () { + var sink = controller.sink.rejectErrors() + ..addStream(StreamController().stream); + expect(() => sink.addError('oh no'), throwsStateError); + }); + + test('throws on addStream()', () { + var sink = controller.sink.rejectErrors() + ..addStream(StreamController().stream); + expect(() => sink.addStream(Stream.empty()), throwsStateError); + }); + + test('throws on close()', () { + var sink = controller.sink.rejectErrors() + ..addStream(StreamController().stream); + expect(() => sink.close(), throwsStateError); + }); + }); +} From b7b51eeab300d840850fe8fb822c4cdf5ad27199 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 27 Apr 2021 16:01:56 -0700 Subject: [PATCH 187/260] Remove extra newlines in CHANGELOG (dart-lang/async#170) --- pkgs/async/CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index e26b7122..d5a22f38 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -2,15 +2,11 @@ * Add a `StreamCloser` class, which is a `StreamTransformer` that allows the caller to force the stream to emit a done event. - * Added `ChunkedStreamReader` for reading _chunked streams_ without managing buffers. - * Add extensions on `StreamSink`, including `StreamSink.transform()` for applying `StreamSinkTransformer`s and `StreamSink.rejectErrors()`. - * Add `StreamGroup.isIdle` and `StreamGroup.onIdle`. - * Add `StreamGroup.isClosed` and `FutureGroup.isClosed` getters. ## 2.5.0 From ad34fb4260eda30e0a63099fcbedf69683f6c21f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 27 Apr 2021 16:05:55 -0700 Subject: [PATCH 188/260] Fix docs in FutureGroup (dart-lang/async#171) There is no `stream` member to reference. --- pkgs/async/lib/src/future_group.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkgs/async/lib/src/future_group.dart b/pkgs/async/lib/src/future_group.dart index c98fbc3c..daf985d3 100644 --- a/pkgs/async/lib/src/future_group.dart +++ b/pkgs/async/lib/src/future_group.dart @@ -51,12 +51,8 @@ class FutureGroup implements Sink> { /// This stream will close when this group is idle *and* [close] has been /// called. /// - /// Note that: - /// - /// * Events won't be emitted on this stream until [stream] has been listened - /// to. - /// * Events are delivered asynchronously, so it's possible for the group to - /// become active again before the event is delivered. + /// Events are delivered asynchronously, so it's possible for the group to + /// become active again before the event is delivered. Stream get onIdle => (_onIdleController ??= StreamController.broadcast(sync: true)).stream; From 67ae6705ee11f2e9372132b1dd709bbd3165826b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 29 Apr 2021 14:49:04 -0700 Subject: [PATCH 189/260] Fix a StreamGroup bug when a component stream's listen() throws (dart-lang/async#173) This would put the StreamGroup into an inconsistent state where it would believe itself to be active, but only some streams would have subscriptions. This was exacerbated by dart-lang/sdkdart-lang/async#45815, which meant that even though _onListen threw an error a StreamSubscription was created and returned, so further callbacks could still be called. Now instead of going into an inconsistent state, the StreamGroup simply cancels itself. --- pkgs/async/CHANGELOG.md | 5 ++ pkgs/async/lib/src/stream_group.dart | 34 +++++++++++--- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_group_test.dart | 63 ++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d5a22f38..6dff1982 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.6.1 + +* When `StreamGroup.stream.listen()` is called, gracefully handle component + streams throwing errors when their `Stream.listen()` methods are called. + ## 2.6.0 * Add a `StreamCloser` class, which is a `StreamTransformer` that allows the diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 49baaad1..6270d9a9 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -4,6 +4,8 @@ import 'dart:async'; +import 'package:collection/collection.dart'; + /// A collection of streams whose events are unified and sent through a central /// stream. /// @@ -185,13 +187,24 @@ class StreamGroup implements Sink> { /// This is called for both single-subscription and broadcast groups. void _onListen() { _state = _StreamGroupState.listening; - _subscriptions.forEach((stream, subscription) { + + for (var entry in _subscriptions.entries.toList()) { // If this is a broadcast group and this isn't the first time it's been // listened to, there may still be some subscriptions to // single-subscription streams. - if (subscription != null) return; - _subscriptions[stream] = _listenToStream(stream); - }); + if (entry.value != null) return; + + var stream = entry.key; + try { + _subscriptions[stream] = _listenToStream(stream); + } catch (error) { + // If [Stream.listen] throws a synchronous error (for example because + // the stream has already been listened to), cancel all subscriptions + // and rethrow the error. + _onCancel()?.catchError((_) {}); + rethrow; + } + } } /// A callback called when [stream] is paused. @@ -216,8 +229,17 @@ class StreamGroup implements Sink> { Future? _onCancel() { _state = _StreamGroupState.canceled; - var futures = _subscriptions.values - .map((subscription) => subscription!.cancel()) + var futures = _subscriptions.entries + .map((entry) { + var subscription = entry.value; + if (subscription != null) return subscription.cancel(); + try { + return entry.key.listen(null).cancel(); + } catch (_) { + return null; + } + }) + .whereNotNull() .toList(); _subscriptions.clear(); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index d645c472..6d5a06da 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.6.0 +version: 2.6.1 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 7ea93f8c..11c66c1e 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -246,6 +246,69 @@ void main() { expect(fired, isTrue); }); }); + + group('when listen() throws an error', () { + late Stream alreadyListened; + setUp(() { + alreadyListened = Stream.value('foo')..listen(null); + }); + + group('listen()', () { + test('rethrows that error', () { + streamGroup.add(alreadyListened); + + // We can't use expect(..., throwsStateError) here bceause of + // dart-lang/sdk#45815. + runZonedGuarded( + () => streamGroup.stream.listen(expectAsync1((_) {}, count: 0)), + expectAsync2((error, _) => expect(error, isStateError))); + }); + + test('cancels other subscriptions', () async { + var firstCancelled = false; + var first = + StreamController(onCancel: () => firstCancelled = true); + streamGroup.add(first.stream); + + streamGroup.add(alreadyListened); + + var lastCancelled = false; + var last = + StreamController(onCancel: () => lastCancelled = true); + streamGroup.add(last.stream); + + runZonedGuarded(() => streamGroup.stream.listen(null), (_, __) {}); + + expect(firstCancelled, isTrue); + expect(lastCancelled, isTrue); + }); + + // There really shouldn't even be a subscription here, but due to + // dart-lang/sdk#45815 there is. + group('canceling the subscription is a no-op', () { + test('synchronously', () { + streamGroup.add(alreadyListened); + + var subscription = runZonedGuarded( + () => streamGroup.stream.listen(null), + expectAsync2((_, __) {}, count: 1)); + + expect(subscription!.cancel(), completes); + }); + + test('asynchronously', () async { + streamGroup.add(alreadyListened); + + var subscription = runZonedGuarded( + () => streamGroup.stream.listen(null), + expectAsync2((_, __) {}, count: 1)); + + await pumpEventQueue(); + expect(subscription!.cancel(), completes); + }); + }); + }); + }); }); group('broadcast', () { From 1d45068a3e83e278471a350c8e5d40333b4068e3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 4 May 2021 13:01:37 -0700 Subject: [PATCH 190/260] Code review changes for dart-lang/async#173 (dart-lang/async#174) I forgot to actually push these to the pull request. --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/stream_group.dart | 4 ++-- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 6dff1982..1560001b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.2 + +* Internal changes only. + ## 2.6.1 * When `StreamGroup.stream.listen()` is called, gracefully handle component diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 6270d9a9..f7819bfd 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -188,7 +188,7 @@ class StreamGroup implements Sink> { void _onListen() { _state = _StreamGroupState.listening; - for (var entry in _subscriptions.entries.toList()) { + for (var entry in [..._subscriptions.entries]) { // If this is a broadcast group and this isn't the first time it's been // listened to, there may still be some subscriptions to // single-subscription streams. @@ -232,8 +232,8 @@ class StreamGroup implements Sink> { var futures = _subscriptions.entries .map((entry) { var subscription = entry.value; - if (subscription != null) return subscription.cancel(); try { + if (subscription != null) return subscription.cancel(); return entry.key.listen(null).cancel(); } catch (_) { return null; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 6d5a06da..4fc5b92e 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.6.1 +version: 2.6.2-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async From f0ee0b477679fee84ba9589a81ddea62a47eaa89 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 7 May 2021 10:33:19 -0700 Subject: [PATCH 191/260] Don't invoke onValue for a canceled operation (dart-lang/async#177) Fixes dart-lang/async#172 The `isCompleted` boolean does not necessarily indicate that `valueOrCancellation` will complete with a value. If the completer was completed with a `Future` and then canceled before that argument invokes the listener callback, the `valueOrCancellation` future will complete with `null` but `isCompleted` will be true. We only have a value to pass to `onValue` when the completer was completed and not canceled. Add tests for the behavior of `then` when `cancel` is called after `completer.complete`, both when it was completed with a Future and a non-Future value. Move the initialization for `originalCompleter` to the `setup` for the tests so that it can be used before calling `then`. --- pkgs/async/CHANGELOG.md | 5 ++-- pkgs/async/lib/src/cancelable_operation.dart | 2 +- .../async/test/cancelable_operation_test.dart | 25 ++++++++++++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1560001b..46806388 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,7 @@ -## 2.6.2 +## 2.6.2-dev -* Internal changes only. +* Fix a bug where `CancelableOperation.then` may invoke the `onValue` callback, + even if it had been canceled before `CancelableOperation.value` completes. ## 2.6.1 diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index c3210787..6e6ef177 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -101,7 +101,7 @@ class CancelableOperation { valueOrCancellation().then((T? result) { if (!completer.isCanceled) { - if (isCompleted) { + if (isCompleted && !isCanceled) { assert(result is T); completer.complete(Future.sync(() => onValue(result as T))); } else if (onCancel != null) { diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 0e8d8d49..47a1e91c 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -267,10 +267,10 @@ void main() { onError = expectAsync2((e, s) => 'Fake', count: 0, id: 'onError'); onCancel = expectAsync0(() => 'Fake', count: 0, id: 'onCancel'); propagateCancel = false; + originalCompleter = CancelableCompleter(); }); CancelableOperation runThen() { - originalCompleter = CancelableCompleter(); return originalCompleter.operation.then(onValue!, onError: onError, onCancel: onCancel, @@ -387,6 +387,29 @@ void main() { expect(runThen().value, throwsA('error')); originalCompleter.operation.cancel(); }); + + test('after completing with a future does not invoke `onValue`', + () async { + onValue = expectAsync1((_) => '', count: 0); + onCancel = null; + var operation = runThen(); + var workCompleter = Completer(); + originalCompleter.complete(workCompleter.future); + originalCompleter.operation.cancel(); + workCompleter.complete(0); + expect(operation.isCanceled, true); + await workCompleter.future; + }); + + test('after the value is completed invokes `onValue`', () { + onValue = expectAsync1((_) => 'foo', count: 1); + onCancel = expectAsync1((_) => '', count: 0); + originalCompleter.complete(0); + originalCompleter.operation.cancel(); + var operation = runThen(); + expect(operation.value, completion('foo')); + expect(operation.isCanceled, false); + }); }); group('returned operation canceled', () { From d84217f42c842d0549d9bdae431aaa8314e3d155 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 11 May 2021 16:50:38 -0700 Subject: [PATCH 192/260] Make isComplete reflect result availability (dart-lang/async#178) Fixes dart-lang/async#176 Change the `CancelableOperation.isComplete` to forward to the `_inner` completer, which is not completed until the result is available unlike `CancelableOperation.isComplete` which may lead the result when `complete` is called with a `Future` argument. Update the docs to reflect the new behavior. Keep the detail about this property indicating whether the operation can still be canceled. This detail was incorrectly stated before, but matches the new implementation. Remove details pointing to `CancelableCompleter` from the `CancelableOperation` docs. Flesh out the docs on the completer so that the distinction between the two `isComplete` getters is more clear. Remove a detail about not returning `null` from the `cancel()` doc since the non-nullable return type already makes this clear. --- pkgs/async/CHANGELOG.md | 2 + pkgs/async/lib/src/cancelable_operation.dart | 59 +++++++++++++------ .../async/test/cancelable_operation_test.dart | 12 +++- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 46806388..47effedb 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -2,6 +2,8 @@ * Fix a bug where `CancelableOperation.then` may invoke the `onValue` callback, even if it had been canceled before `CancelableOperation.value` completes. +* Fix a bug in `CancelableOperation.isComplete` where it may appear to be + complete and no longer be cancelable when it in fact could still be canceled. ## 2.6.1 diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 6e6ef177..e04af905 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -31,9 +31,7 @@ class CancelableOperation { /// It's guaranteed to only be called once. /// /// Calling this constructor is equivalent to creating a [CancelableCompleter] - /// and completing it with [inner]. As such, [isCompleted] is true from the - /// moment this [CancelableOperation] is created, regardless of whether - /// [inner] has completed yet or not. + /// and completing it with [inner]. factory CancelableOperation.fromFuture(Future inner, {FutureOr Function()? onCancel}) { var completer = CancelableCompleter(onCancel: onCancel); @@ -124,20 +122,19 @@ class CancelableOperation { /// Cancels this operation. /// - /// This returns the [Future] returned by the [CancelableCompleter]'s - /// `onCancel` callback. Unlike [Stream.cancel], it never returns `null`. + /// If this operation [isComplete] or [isCanceled] this call is ignored. + /// Returns the result of the `onCancel` callback, if one exists. Future cancel() => _completer._cancel(); /// Whether this operation has been canceled before it completed. bool get isCanceled => _completer.isCanceled; - /// Whether the [CancelableCompleter] backing this operation has been - /// completed. + /// Whether the result of this operation is ready. /// - /// This value being true does not imply that the [value] future has - /// completed, but merely that it is no longer possible to [cancel] the - /// operation. - bool get isCompleted => _completer.isCompleted; + /// When ready, the [value] future is completed with the result value + /// or error, and this operation can no longer be cancelled. + /// An operation may be complete before the listeners on [value] are invoked. + bool get isCompleted => _completer._inner.isCompleted; } /// A completer for a [CancelableOperation]. @@ -161,11 +158,18 @@ class CancelableCompleter { /// The operation controlled by this completer. late final operation = CancelableOperation._(this); - /// Whether the completer has completed. + /// Whether the [complete] or [completeError] have been called. + /// + /// Once this completer has been completed with either a result or error, + /// neither method may be called again. + /// + /// If [complete] was called with a [Future] argument, this completer may be + /// completed before it's [operation] is completed. In that case the + /// [operation] may still be canceled before the result is available. bool get isCompleted => _isCompleted; bool _isCompleted = false; - /// Whether the completer was canceled before being completed. + /// Whether the completer was canceled before the result was ready. bool get isCanceled => _isCanceled; bool _isCanceled = false; @@ -174,8 +178,15 @@ class CancelableCompleter { /// Completes [operation] to [value]. /// - /// If [value] is a [Future], this will complete to the result of that - /// [Future] once it completes. + /// If [value] is a [Future] the [operation] will complete with the result of + /// that `Future` once it is available. + /// In that case [isComplete] will be true before the [operation] is complete. + /// + /// If the type [T] is not nullable [value] may be not be omitted or `null`. + /// + /// This method may not be called after either [complete] or [completeError] + /// has been called once. + /// The [isCompleted] is true when either of these methods have been called. void complete([FutureOr? value]) { if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; @@ -202,7 +213,11 @@ class CancelableCompleter { }); } - /// Completes [operation] to [error]. + /// Completes [operation] with [error] and [stackTrace]. + /// + /// This method may not be called after either [complete] or [completeError] + /// has been called once. + /// The [isCompleted] is true when either of these methods have been called. void completeError(Object error, [StackTrace? stackTrace]) { if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; @@ -211,7 +226,17 @@ class CancelableCompleter { _inner.completeError(error, stackTrace); } - /// Cancel the completer. + /// Cancel the operation. + /// + /// This call is be ignored if the result of the operation is already + /// available. + /// The result of the operation may only be available some time after + /// the completer has been completed (using [complete] or [completeError], + /// which sets [isCompleted] to true) if completed with a [Future]. + /// The completer can be cancelled until the result becomes available, + /// even if [isCompleted] is true. + /// + /// This call is ignored if this completer has already been canceled. Future _cancel() { if (_inner.isCompleted) return Future.value(); diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 47a1e91c..a46dfe2b 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -44,12 +44,13 @@ void main() { expect(completer.isCompleted, isTrue); }); - test('sends errors in a future to the future', () { + test('sends errors in a future to the future', () async { expect(completer.operation.value, throwsA('error')); expect(completer.isCompleted, isFalse); expect(completer.operation.isCompleted, isFalse); completer.complete(Future.error('error')); expect(completer.isCompleted, isTrue); + await flushMicrotasks(); expect(completer.operation.isCompleted, isTrue); }); @@ -68,6 +69,15 @@ void main() { expect(await operation.then((_) {}).value, null); }); + test('is not complete until the result is available', () async { + var backingWork = Completer(); + var operation = CancelableOperation.fromFuture(backingWork.future); + expect(operation.isCompleted, isFalse); + backingWork.complete(); + await backingWork.future; + expect(operation.isCompleted, isTrue); + }); + group('throws a StateError if completed', () { test('successfully twice', () { completer.complete(1); From 34893b35210e33e2997be9641e12b17f535a35d1 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 18 May 2021 12:28:55 -0700 Subject: [PATCH 193/260] Add a Stream.slices extension method (dart-lang/async#175) --- pkgs/async/CHANGELOG.md | 4 +- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/stream_extensions.dart | 24 +++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_extensions_test.dart | 58 +++++++++++++++++++++ 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 pkgs/async/lib/src/stream_extensions.dart create mode 100644 pkgs/async/test/stream_extensions_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 47effedb..712352e1 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,6 @@ -## 2.6.2-dev +## 2.7.0 + +* Add a `Stream.slices()` extension method. * Fix a bug where `CancelableOperation.then` may invoke the `onValue` callback, even if it had been canceled before `CancelableOperation.value` completes. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 2d5876a0..21704422 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -29,6 +29,7 @@ export 'src/result/value.dart'; export 'src/single_subscription_transformer.dart'; export 'src/stream_closer.dart'; export 'src/stream_completer.dart'; +export 'src/stream_extensions.dart'; export 'src/stream_group.dart'; export 'src/stream_queue.dart'; export 'src/stream_sink_completer.dart'; diff --git a/pkgs/async/lib/src/stream_extensions.dart b/pkgs/async/lib/src/stream_extensions.dart new file mode 100644 index 00000000..8bd4b017 --- /dev/null +++ b/pkgs/async/lib/src/stream_extensions.dart @@ -0,0 +1,24 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; + +/// Utility extensions on [Stream]. +extension StreamExtensions on Stream { + Stream> slices(int length) { + if (length < 1) throw RangeError.range(length, 1, null, 'length'); + + var slice = []; + return transform(StreamTransformer.fromHandlers(handleData: (data, sink) { + slice.add(data); + if (slice.length == length) { + sink.add(slice); + slice = []; + } + }, handleDone: (sink) { + if (slice.isNotEmpty) sink.add(slice); + sink.close(); + })); + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 4fc5b92e..b6b6bcaa 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.6.2-dev +version: 2.7.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/stream_extensions_test.dart b/pkgs/async/test/stream_extensions_test.dart new file mode 100644 index 00000000..85a3cee2 --- /dev/null +++ b/pkgs/async/test/stream_extensions_test.dart @@ -0,0 +1,58 @@ +// Copyright (c) 2015, 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 filevents. + +import 'dart:async'; + +import 'package:async/async.dart'; +import 'package:test/test.dart'; + +void main() { + group('.slices', () { + test('empty', () { + expect(Stream.empty().slices(1).toList(), completion(equals([]))); + }); + + test('with the same length as the iterable', () { + expect( + Stream.fromIterable([1, 2, 3]).slices(3).toList(), + completion(equals([ + [1, 2, 3] + ]))); + }); + + test('with a longer length than the iterable', () { + expect( + Stream.fromIterable([1, 2, 3]).slices(5).toList(), + completion(equals([ + [1, 2, 3] + ]))); + }); + + test('with a shorter length than the iterable', () { + expect( + Stream.fromIterable([1, 2, 3]).slices(2).toList(), + completion(equals([ + [1, 2], + [3] + ]))); + }); + + test('with length divisible by the iterable\'s', () { + expect( + Stream.fromIterable([1, 2, 3, 4]).slices(2).toList(), + completion(equals([ + [1, 2], + [3, 4] + ]))); + }); + + test('refuses negative length', () { + expect(() => Stream.fromIterable([1]).slices(-1), throwsRangeError); + }); + + test('refuses length 0', () { + expect(() => Stream.fromIterable([1]).slices(0), throwsRangeError); + }); + }); +} From b1f1e0d1db97e0ec8da0fca8860fa790421c2087 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 20 May 2021 11:00:42 -0700 Subject: [PATCH 194/260] Add doc comment on Stream.slices (dart-lang/async#180) Based on the doc comment on the unmerged Iterable.slices from https://github.com/dart-lang/collection/pull/191 Drop extra newline in changelog, sort deps, and drop `-nullsafety` from constraints. --- pkgs/async/CHANGELOG.md | 1 - pkgs/async/lib/src/stream_extensions.dart | 12 ++++++++++++ pkgs/async/pubspec.yaml | 10 +++++----- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 712352e1..58d6f9b5 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,7 +1,6 @@ ## 2.7.0 * Add a `Stream.slices()` extension method. - * Fix a bug where `CancelableOperation.then` may invoke the `onValue` callback, even if it had been canceled before `CancelableOperation.value` completes. * Fix a bug in `CancelableOperation.isComplete` where it may appear to be diff --git a/pkgs/async/lib/src/stream_extensions.dart b/pkgs/async/lib/src/stream_extensions.dart index 8bd4b017..3801a025 100644 --- a/pkgs/async/lib/src/stream_extensions.dart +++ b/pkgs/async/lib/src/stream_extensions.dart @@ -6,6 +6,18 @@ import 'dart:async'; /// Utility extensions on [Stream]. extension StreamExtensions on Stream { + /// Creates a stream whose elements are contiguous slices of [this]. + /// + /// Each slice is [length] elements long, except for the last one which may be + /// shorter if [this] emits too few elements. Each slice begins after the + /// last one ends. + /// + /// For example, `Stream.fromIterable([1, 2, 3, 4, 5]).slices(2)` emits + /// `([1, 2], [3, 4], [5])`. + /// + /// Errors are forwarded to the result stream immediately when they occur, + /// even if previous data events have not been emitted because the next slice + /// is not complete yet. Stream> slices(int length) { if (length < 1) throw RangeError.range(length, 1, null, 'length'); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b6b6bcaa..13e7281c 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -8,11 +8,11 @@ environment: sdk: ">=2.12.0 <3.0.0" dependencies: - meta: ^1.1.7 collection: ^1.15.0 + meta: ^1.1.7 dev_dependencies: - fake_async: ^1.2.0-nullsafety - stack_trace: ^1.10.0-nullsafety - test: ^1.16.0-nullsafety - pedantic: ^1.10.0-nullsafety + fake_async: ^1.2.0 + pedantic: ^1.10.0 + stack_trace: ^1.10.0 + test: ^1.16.0 From 972f8bf68d650e2f430f3f0e6196eb39225db8ee Mon Sep 17 00:00:00 2001 From: Jonas Finnemann Jensen Date: Thu, 17 Jun 2021 14:46:41 +0200 Subject: [PATCH 195/260] Faster ChunkedStreamReader. (dart-lang/async#182) * Faster ChunkedStreamReader. * Add an internal `_offset` to track offset in `_buffer`, reducing the number of times we need to create a sublist internally. * Specialize to handle cases where `_buffer` is a `Uint8List` by creating a `Uint8List.sublistView` when we need to split a chunk. --- pkgs/async/CHANGELOG.md | 5 + pkgs/async/lib/src/chunked_stream_reader.dart | 63 ++++++++--- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/chunked_stream_reader.dart | 102 ++++++++++++++++++ 4 files changed, 159 insertions(+), 13 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 58d6f9b5..2e366fee 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.7.0-dev + +* Improve performance for `ChunkedStreamReader` by creating fewer internal + sublists and specializing to create views for `Uint8List` chunks. + ## 2.7.0 * Add a `Stream.slices()` extension method. diff --git a/pkgs/async/lib/src/chunked_stream_reader.dart b/pkgs/async/lib/src/chunked_stream_reader.dart index 855a7724..a8963411 100644 --- a/pkgs/async/lib/src/chunked_stream_reader.dart +++ b/pkgs/async/lib/src/chunked_stream_reader.dart @@ -38,9 +38,34 @@ import 'byte_collector.dart' show collectBytes; /// The read-operations [readChunk] and [readStream] must not be invoked until /// the future from a previous call has completed. class ChunkedStreamReader { + /// Iterator over underlying stream. + /// + /// The reader requests data from this input whenever requests on the + /// reader cannot be fulfilled with the already fetched data. final StreamIterator> _input; + + /// Sentinel value used for [_buffer] when we have no value. final List _emptyList = const []; + + /// Last partially consumed chunk received from [_input]. + /// + /// Elements up to [_offset] have already been consumed and should not be + /// consumed again. List _buffer = []; + + /// Offset into [_buffer] after data which have already been emitted. + /// + /// The offset is between `0` and `_buffer.length`, both inclusive. + /// The data in [_buffer] from [_offset] and forward have not yet been + /// emitted by the chunked stream reader, the data before [_offset] has. + int _offset = 0; + + /// Whether a read request is currently being processed. + /// + /// Is `true` while a request is in progress. + /// While a read request, like [readChunk] or [readStream], is being processed, + /// no new requests can be made. + /// New read attempts will throw instead. bool _reading = false; factory ChunkedStreamReader(Stream> stream) => @@ -96,8 +121,9 @@ class ChunkedStreamReader { final substream = () async* { // While we have data to read while (size > 0) { - // Read something into the buffer, if it's empty - if (_buffer.isEmpty) { + // Read something into the buffer, if buffer has been consumed. + assert(_offset <= _buffer.length); + if (_offset == _buffer.length) { if (!(await _input.moveNext())) { // Don't attempt to read more data, as there is no more data. size = 0; @@ -105,21 +131,30 @@ class ChunkedStreamReader { break; } _buffer = _input.current; + _offset = 0; } - if (_buffer.isNotEmpty) { - if (size < _buffer.length) { - final output = _buffer.sublist(0, size); - _buffer = _buffer.sublist(size); + final remainingBuffer = _buffer.length - _offset; + if (remainingBuffer > 0) { + if (remainingBuffer >= size) { + List output; + if (_buffer is Uint8List) { + output = Uint8List.sublistView( + _buffer as Uint8List, _offset, _offset + size) as List; + } else { + output = _buffer.sublist(_offset, _offset + size); + } + _offset += size; size = 0; yield output; _reading = false; break; } - final output = _buffer; - size -= _buffer.length; + final output = _offset == 0 ? _buffer : _buffer.sublist(_offset); + size -= remainingBuffer; _buffer = _emptyList; + _offset = 0; yield output; } } @@ -129,22 +164,26 @@ class ChunkedStreamReader { c.onListen = () => c.addStream(substream()).whenComplete(c.close); c.onCancel = () async { while (size > 0) { - if (_buffer.isEmpty) { + assert(_offset <= _buffer.length); + if (_buffer.length == _offset) { if (!await _input.moveNext()) { size = 0; // no more data break; } _buffer = _input.current; + _offset = 0; } - if (size < _buffer.length) { - _buffer = _buffer.sublist(size); + final remainingBuffer = _buffer.length - _offset; + if (remainingBuffer >= size) { + _offset += size; size = 0; break; } - size -= _buffer.length; + size -= remainingBuffer; _buffer = _emptyList; + _offset = 0; } _reading = false; }; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 13e7281c..79dc2786 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.7.0 +version: 2.7.1-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/chunked_stream_reader.dart b/pkgs/async/test/chunked_stream_reader.dart index 7dcd4083..d80cf837 100644 --- a/pkgs/async/test/chunked_stream_reader.dart +++ b/pkgs/async/test/chunked_stream_reader.dart @@ -377,4 +377,106 @@ void main() { expect(await collectBytes(stream), hasLength(lessThan(2))); }); + + test('readChunk() chunk by chunk (Uint8List)', () async { + final r = ChunkedStreamReader(() async* { + yield Uint8List.fromList([1, 2]); + yield Uint8List.fromList([3, 4, 5]); + yield Uint8List.fromList([6, 7, 8, 9]); + yield Uint8List.fromList([10]); + }()); + + expect(await r.readChunk(2), equals([1, 2])); + expect(await r.readChunk(3), equals([3, 4, 5])); + expect(await r.readChunk(4), equals([6, 7, 8, 9])); + expect(await r.readChunk(1), equals([10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() element by element (Uint8List)', () async { + final r = ChunkedStreamReader(() async* { + yield Uint8List.fromList([1, 2]); + yield Uint8List.fromList([3, 4, 5]); + yield Uint8List.fromList([6, 7, 8, 9]); + yield Uint8List.fromList([10]); + }()); + + for (var i = 0; i < 10; i++) { + expect(await r.readChunk(1), equals([i + 1])); + } + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() exact elements (Uint8List)', () async { + final r = ChunkedStreamReader(() async* { + yield Uint8List.fromList([1, 2]); + yield Uint8List.fromList([3, 4, 5]); + yield Uint8List.fromList([6, 7, 8, 9]); + yield Uint8List.fromList([10]); + }()); + + expect(await r.readChunk(10), equals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() past end (Uint8List)', () async { + final r = ChunkedStreamReader(() async* { + yield Uint8List.fromList([1, 2]); + yield Uint8List.fromList([3, 4, 5]); + yield Uint8List.fromList([6, 7, 8, 9]); + yield Uint8List.fromList([10]); + }()); + + expect(await r.readChunk(20), equals([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() chunks of 2 elements (Uint8List)', () async { + final r = ChunkedStreamReader(() async* { + yield Uint8List.fromList([1, 2]); + yield Uint8List.fromList([3, 4, 5]); + yield Uint8List.fromList([6, 7, 8, 9]); + yield Uint8List.fromList([10]); + }()); + + expect(await r.readChunk(2), equals([1, 2])); + expect(await r.readChunk(2), equals([3, 4])); + expect(await r.readChunk(2), equals([5, 6])); + expect(await r.readChunk(2), equals([7, 8])); + expect(await r.readChunk(2), equals([9, 10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); + + test('readChunk() chunks of 3 elements (Uint8List)', () async { + final r = ChunkedStreamReader(() async* { + yield Uint8List.fromList([1, 2]); + yield Uint8List.fromList([3, 4, 5]); + yield Uint8List.fromList([6, 7, 8, 9]); + yield Uint8List.fromList([10]); + }()); + + expect(await r.readChunk(3), equals([1, 2, 3])); + expect(await r.readChunk(3), equals([4, 5, 6])); + expect(await r.readChunk(3), equals([7, 8, 9])); + expect(await r.readChunk(3), equals([10])); + expect(await r.readChunk(1), equals([])); + expect(await r.readChunk(1), equals([])); + await r.cancel(); // check this is okay! + expect(await r.readChunk(1), equals([])); + }); } From 3b6ab88e17da40d47098fd765c4c40e15e226f05 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 29 Jun 2021 10:32:00 -0700 Subject: [PATCH 196/260] Use BytesBuilder for collectBytes (dart-lang/async#185) When this utility was first written `BytesBuilder` was only importable from `dart:io`. Now that it may be imported from the cross platform `dart:typed_data` it can be used in `package:async`. --- pkgs/async/CHANGELOG.md | 2 +- pkgs/async/lib/src/byte_collector.dart | 29 +++++--------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 2e366fee..51c067d5 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.7.0-dev +## 2.7.1-dev * Improve performance for `ChunkedStreamReader` by creating fewer internal sublists and specializing to create views for `Uint8List` chunks. diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart index 0226dea4..a7a54358 100644 --- a/pkgs/async/lib/src/byte_collector.dart +++ b/pkgs/async/lib/src/byte_collector.dart @@ -42,30 +42,11 @@ CancelableOperation collectBytesCancelable( /// so it can cancel the operation. T _collectBytes(Stream> source, T Function(StreamSubscription>, Future) result) { - var byteLists = >[]; - var length = 0; + var bytes = BytesBuilder(); var completer = Completer.sync(); - var subscription = source.listen( - (bytes) { - byteLists.add(bytes); - length += bytes.length; - }, - onError: completer.completeError, - onDone: () { - completer.complete(_collect(length, byteLists)); - }, - cancelOnError: true); + var subscription = + source.listen(bytes.add, onError: completer.completeError, onDone: () { + completer.complete(bytes.takeBytes()); + }, cancelOnError: true); return result(subscription, completer.future); } - -// Join a lists of bytes with a known total length into a single [Uint8List]. -Uint8List _collect(int length, List> byteLists) { - var result = Uint8List(length); - var i = 0; - for (var byteList in byteLists) { - var end = i + byteList.length; - result.setRange(i, end, byteList); - i = end; - } - return result; -} From b1b2cfa8bd2a42d1d9d85ad3651fd59d35c7a78d Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 1 Jul 2021 14:13:30 -0700 Subject: [PATCH 197/260] Avoid eager copying with BytesBuilder (dart-lang/async#186) The previous implementation of `collectBytes` would return a `Uint8List` where the `lengthInBytes` and `buffer.lengthInBytes` matched. This property isn't an intended guarantee, however since some callers may rely on it for correct behavior it is safer to retain it. Use `copy: false` so that the buffer used in `takeBytes` has the same length as the data. --- pkgs/async/lib/src/byte_collector.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/lib/src/byte_collector.dart b/pkgs/async/lib/src/byte_collector.dart index a7a54358..0c937616 100644 --- a/pkgs/async/lib/src/byte_collector.dart +++ b/pkgs/async/lib/src/byte_collector.dart @@ -42,7 +42,7 @@ CancelableOperation collectBytesCancelable( /// so it can cancel the operation. T _collectBytes(Stream> source, T Function(StreamSubscription>, Future) result) { - var bytes = BytesBuilder(); + var bytes = BytesBuilder(copy: false); var completer = Completer.sync(); var subscription = source.listen(bytes.add, onError: completer.completeError, onDone: () { From 2db99c1972207540e7757ee213003a15cfc302dc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 12 Jul 2021 07:39:48 -0700 Subject: [PATCH 198/260] Dart format with latest SDK (dart-lang/async#189) --- pkgs/async/test/reject_errors_test.dart | 5 ++++- pkgs/async/test/stream_completer_test.dart | 6 +++++- .../test/stream_sink_completer_test.dart | 20 ++++++++++++++---- pkgs/async/test/stream_zip_test.dart | 21 +++++++++++++++---- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/pkgs/async/test/reject_errors_test.dart b/pkgs/async/test/reject_errors_test.dart index 32bffd15..5b8b3e77 100644 --- a/pkgs/async/test/reject_errors_test.dart +++ b/pkgs/async/test/reject_errors_test.dart @@ -14,7 +14,10 @@ void main() { }); test('passes through data events', () { - controller.sink.rejectErrors()..add(1)..add(2)..add(3); + controller.sink.rejectErrors() + ..add(1) + ..add(2) + ..add(3); expect(controller.stream, emitsInOrder([1, 2, 3])); }); diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index ecb56215..5ccf44e0 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -39,7 +39,11 @@ void main() { var done = subscription.asFuture(); subscription.pause(); var sourceController = StreamController(); - sourceController..add(1)..add(2)..add(3)..add(4); + sourceController + ..add(1) + ..add(2) + ..add(3) + ..add(4); controller.setSourceStream(sourceController.stream); await flushMicrotasks(); expect(sourceController.hasListener, isTrue); diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 591f32fc..11592bb7 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -19,7 +19,11 @@ void main() { test('data events are forwarded', () { var sink = TestSink(); completer.setDestinationSink(sink); - completer.sink..add(1)..add(2)..add(3)..add(4); + completer.sink + ..add(1) + ..add(2) + ..add(3) + ..add(4); expect(sink.results[0].asValue!.value, equals(1)); expect(sink.results[1].asValue!.value, equals(2)); @@ -30,7 +34,9 @@ void main() { test('error events are forwarded', () { var sink = TestSink(); completer.setDestinationSink(sink); - completer.sink..addError('oh no')..addError("that's bad"); + completer.sink + ..addError('oh no') + ..addError("that's bad"); expect(sink.results[0].asError!.error, equals('oh no')); expect(sink.results[1].asError!.error, equals("that's bad")); @@ -114,7 +120,11 @@ void main() { group('when a stream is linked after events are added', () { test('data events are forwarded', () async { - completer.sink..add(1)..add(2)..add(3)..add(4); + completer.sink + ..add(1) + ..add(2) + ..add(3) + ..add(4); await flushMicrotasks(); var sink = TestSink(); @@ -128,7 +138,9 @@ void main() { }); test('error events are forwarded', () async { - completer.sink..addError('oh no')..addError("that's bad"); + completer.sink + ..addError('oh no') + ..addError("that's bad"); await flushMicrotasks(); var sink = TestSink(); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 84948bff..71ce2c46 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -210,15 +210,26 @@ void main() { expect( StreamZip([ streamError(mks([1, 2, 3, 4]), 4, 'BAD-5'), - (StreamController()..add(4)..add(5)..add(6)).stream, - (StreamController()..add(7)..add(8)..add(9)).stream + (StreamController() + ..add(4) + ..add(5) + ..add(6)) + .stream, + (StreamController() + ..add(7) + ..add(8) + ..add(9)) + .stream ]).toList(), throwsA(equals('BAD-5'))); }); test('Error after first end', () { var controller = StreamController(); - controller..add(7)..add(8)..add(9); + controller + ..add(7) + ..add(8) + ..add(9); // Transformer that puts error into controller when one of the first two // streams have sent a done event. var trans = @@ -298,7 +309,9 @@ void main() { ..add(5) ..add(7) ..close(); - c2..add(2)..add(4); + c2 + ..add(2) + ..add(4); }); test('pause-resume2', () { From 52207858bc7d6632410ef8f75b84e49376f162f6 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Jul 2021 18:05:04 +0000 Subject: [PATCH 199/260] Add *SinkBase classes for implementing custom sinks (dart-lang/async#188) --- pkgs/async/CHANGELOG.md | 4 +- pkgs/async/lib/async.dart | 1 + pkgs/async/lib/src/sink_base.dart | 168 ++++++++++++ pkgs/async/pubspec.yaml | 3 +- pkgs/async/test/io_sink_impl.dart | 23 ++ pkgs/async/test/sink_base_test.dart | 403 ++++++++++++++++++++++++++++ 6 files changed, 600 insertions(+), 2 deletions(-) create mode 100644 pkgs/async/lib/src/sink_base.dart create mode 100644 pkgs/async/test/io_sink_impl.dart create mode 100644 pkgs/async/test/sink_base_test.dart diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 51c067d5..102ad59f 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,5 +1,7 @@ -## 2.7.1-dev +## 2.8.0 +* Add `EventSinkBase`, `StreamSinkBase`, and `IOSinkBase` classes to make it + easier to implement custom sinks. * Improve performance for `ChunkedStreamReader` by creating fewer internal sublists and specializing to create views for `Uint8List` chunks. diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 21704422..10e7fe79 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -27,6 +27,7 @@ export 'src/result/error.dart'; export 'src/result/future.dart'; export 'src/result/value.dart'; export 'src/single_subscription_transformer.dart'; +export 'src/sink_base.dart'; export 'src/stream_closer.dart'; export 'src/stream_completer.dart'; export 'src/stream_extensions.dart'; diff --git a/pkgs/async/lib/src/sink_base.dart b/pkgs/async/lib/src/sink_base.dart new file mode 100644 index 00000000..873ce823 --- /dev/null +++ b/pkgs/async/lib/src/sink_base.dart @@ -0,0 +1,168 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; +import 'dart:convert'; + +import 'package:meta/meta.dart'; + +import 'async_memoizer.dart'; + +/// An abstract class that implements [EventSink] in terms of [onData], +/// [onError], and [onClose] methods. +/// +/// This takes care of ensuring that events can't be added after [close] is +/// called. +abstract class EventSinkBase implements EventSink { + /// Whether [close] has been called and no more events may be written. + bool get _closed => _closeMemo.hasRun; + + @override + void add(T data) { + _checkCanAddEvent(); + onAdd(data); + } + + /// A method that handles data events that are passed to the sink. + @visibleForOverriding + void onAdd(T data); + + @override + void addError(Object error, [StackTrace? stackTrace]) { + _checkCanAddEvent(); + onError(error, stackTrace); + } + + /// A method that handles error events that are passed to the sink. + @visibleForOverriding + void onError(Object error, [StackTrace? stackTrace]); + + @override + Future close() => _closeMemo.runOnce(onClose); + final _closeMemo = AsyncMemoizer(); + + /// A method that handles the sink being closed. + /// + /// This may return a future that completes once the stream sink has shut + /// down. If cleaning up can fail, the error may be reported in the returned + /// future. + @visibleForOverriding + FutureOr onClose(); + + /// Asserts that the sink is in a state where adding an event is valid. + void _checkCanAddEvent() { + if (_closed) throw StateError('Cannot add event after closing'); + } +} + +/// An abstract class that implements [StreamSink] in terms of [onData], +/// [onError], and [onClose] methods. +/// +/// This takes care of ensuring that events can't be added after [close] is +/// called or during a call to [onStream]. +abstract class StreamSinkBase extends EventSinkBase + implements StreamSink { + /// Whether a call to [addStream] is ongoing. + bool _addingStream = false; + + @override + Future get done => _closeMemo.future; + + @override + Future addStream(Stream stream) { + _checkCanAddEvent(); + + _addingStream = true; + var completer = Completer.sync(); + stream.listen(onAdd, onError: onError, onDone: () { + _addingStream = false; + completer.complete(); + }); + return completer.future; + } + + @override + Future close() { + if (_addingStream) throw StateError('StreamSink is bound to a stream'); + return super.close(); + } + + @override + void _checkCanAddEvent() { + super._checkCanAddEvent(); + if (_addingStream) throw StateError('StreamSink is bound to a stream'); + } +} + +/// An abstract class that implements `dart:io`'s [IOSink]'s API in terms of +/// [onData], [onError], [onClose], and [onFlush] methods. +/// +/// Because [IOSink] is defined in `dart:io`, this can't officially implement +/// it. However, it's designed to match its API exactly so that subclasses can +/// implement [IOSink] without any additional modifications. +/// +/// This takes care of ensuring that events can't be added after [close] is +/// called or during a call to [onStream]. +abstract class IOSinkBase extends StreamSinkBase> { + /// See [IOSink.encoding] from `dart:io`. + Encoding encoding; + + IOSinkBase([this.encoding = utf8]); + + /// See [IOSink.flush] from `dart:io`. + /// + /// Because this base class doesn't do any buffering of its own, [flush] + /// always completes immediately. + /// + /// Subclasses that do buffer events should override [flush] to complete once + /// all events are delivered. They should also call `super.flush()` at the + /// beginning of the method to throw a [StateError] if the sink is currently + /// adding a stream. + Future flush() { + if (_addingStream) throw StateError('StreamSink is bound to a stream'); + if (_closed) return Future.value(); + + _addingStream = true; + return onFlush().whenComplete(() { + _addingStream = false; + }); + } + + /// Flushes any buffered data to the underlying consumer, and returns a future + /// that completes once the consumer has accepted all data. + @visibleForOverriding + Future onFlush(); + + /// See [IOSink.write] from `dart:io`. + void write(Object? object) { + var string = object.toString(); + if (string.isEmpty) return; + add(encoding.encode(string)); + } + + /// See [IOSink.writeAll] from `dart:io`. + void writeAll(Iterable objects, [String separator = '']) { + var first = true; + for (var object in objects) { + if (first) { + first = false; + } else { + write(separator); + } + + write(object); + } + } + + /// See [IOSink.writeln] from `dart:io`. + void writeln([Object? object = '']) { + write(object); + write('\n'); + } + + /// See [IOSink.writeCharCode] from `dart:io`. + void writeCharCode(int charCode) { + write(String.fromCharCode(charCode)); + } +} diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 79dc2786..b0a68a67 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.7.1-dev +version: 2.8.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async @@ -12,6 +12,7 @@ dependencies: meta: ^1.1.7 dev_dependencies: + charcode: ^1.3.0 fake_async: ^1.2.0 pedantic: ^1.10.0 stack_trace: ^1.10.0 diff --git a/pkgs/async/test/io_sink_impl.dart b/pkgs/async/test/io_sink_impl.dart new file mode 100644 index 00000000..63fa289d --- /dev/null +++ b/pkgs/async/test/io_sink_impl.dart @@ -0,0 +1,23 @@ +// Copyright (c) 2021, 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. + +import 'dart:io'; + +import 'package:async/async.dart'; + +/// This class isn't used, it's just used to verify that [IOSinkBase] produces a +/// valid implementation of [IOSink]. +class IOSinkImpl extends IOSinkBase implements IOSink { + @override + void onAdd(List data) {} + + @override + void onError(Object error, [StackTrace? stackTrace]) {} + + @override + void onClose() {} + + @override + Future onFlush() => Future.value(); +} diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart new file mode 100644 index 00000000..da3ca9fb --- /dev/null +++ b/pkgs/async/test/sink_base_test.dart @@ -0,0 +1,403 @@ +// Copyright (c) 2021, 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. + +import 'dart:async'; +import 'dart:convert'; + +import 'package:charcode/charcode.dart'; +import 'package:test/test.dart'; + +import 'package:async/async.dart'; + +void main() { + // We don't explicitly test [EventSinkBase] because it shares all the relevant + // implementation with [StreamSinkBase]. + group('StreamSinkBase', () { + test('forwards add() to onAdd()', () { + var sink = _StreamSink(onAdd: expectAsync1((value) { + expect(value, equals(123)); + })); + sink.add(123); + }); + + test('forwards addError() to onError()', () { + var sink = _StreamSink(onError: expectAsync2((error, [stackTrace]) { + expect(error, equals('oh no')); + expect(stackTrace, isA()); + })); + sink.addError('oh no', StackTrace.current); + }); + + test('forwards addStream() to onAdd() and onError()', () { + var sink = _StreamSink( + onAdd: expectAsync1((value) { + expect(value, equals(123)); + }, count: 1), + onError: expectAsync2((error, [stackTrace]) { + expect(error, equals('oh no')); + expect(stackTrace, isA()); + })); + + var controller = StreamController(); + sink.addStream(controller.stream); + + controller.add(123); + controller.addError('oh no', StackTrace.current); + }); + + test('addStream() returns once the stream closes', () async { + var sink = _StreamSink(); + var controller = StreamController(); + var addStreamCompleted = false; + sink.addStream(controller.stream).then((_) => addStreamCompleted = true); + ; + + await pumpEventQueue(); + expect(addStreamCompleted, isFalse); + + controller.addError('oh no', StackTrace.current); + await pumpEventQueue(); + expect(addStreamCompleted, isFalse); + + controller.close(); + await pumpEventQueue(); + expect(addStreamCompleted, isTrue); + }); + + test('forwards close() to onClose()', () { + var sink = _StreamSink(onClose: expectAsync0(() {})); + expect(sink.close(), completes); + }); + + test('onClose() is only invoked once', () { + var sink = _StreamSink(onClose: expectAsync0(() {}, count: 1)); + expect(sink.close(), completes); + expect(sink.close(), completes); + expect(sink.close(), completes); + }); + + test('all invocations of close() return the same future', () async { + var completer = Completer(); + var sink = _StreamSink(onClose: expectAsync0(() => completer.future)); + + var close1Completed = false; + sink.close().then((_) => close1Completed = true); + + var close2Completed = false; + sink.close().then((_) => close2Completed = true); + + var doneCompleted = false; + sink.done.then((_) => doneCompleted = true); + + await pumpEventQueue(); + expect(close1Completed, isFalse); + expect(close2Completed, isFalse); + expect(doneCompleted, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(close1Completed, isTrue); + expect(close2Completed, isTrue); + expect(doneCompleted, isTrue); + }); + + test('done returns a future that completes once close() completes', + () async { + var completer = Completer(); + var sink = _StreamSink(onClose: expectAsync0(() => completer.future)); + + var doneCompleted = false; + sink.done.then((_) => doneCompleted = true); + + await pumpEventQueue(); + expect(doneCompleted, isFalse); + + expect(sink.close(), completes); + await pumpEventQueue(); + expect(doneCompleted, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(doneCompleted, isTrue); + }); + + group('during addStream()', () { + test('add() throws an error', () { + var sink = _StreamSink(onAdd: expectAsync1((_) {}, count: 0)); + sink.addStream(StreamController().stream); + expect(() => sink.add(1), throwsStateError); + }); + + test('addError() throws an error', () { + var sink = _StreamSink(onError: expectAsync2((_, [__]) {}, count: 0)); + sink.addStream(StreamController().stream); + expect(() => sink.addError('oh no'), throwsStateError); + }); + + test('addStream() throws an error', () { + var sink = _StreamSink(onAdd: expectAsync1((_) {}, count: 0)); + sink.addStream(StreamController().stream); + expect(() => sink.addStream(Stream.value(123)), throwsStateError); + }); + + test('close() throws an error', () { + var sink = _StreamSink(onClose: expectAsync0(() {}, count: 0)); + sink.addStream(StreamController().stream); + expect(() => sink.close(), throwsStateError); + }); + }); + + group("once it's closed", () { + test('add() throws an error', () { + var sink = _StreamSink(onAdd: expectAsync1((_) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.add(1), throwsStateError); + }); + + test('addError() throws an error', () { + var sink = _StreamSink(onError: expectAsync2((_, [__]) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.addError('oh no'), throwsStateError); + }); + + test('addStream() throws an error', () { + var sink = _StreamSink(onAdd: expectAsync1((_) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.addStream(Stream.value(123)), throwsStateError); + }); + }); + }); + + group('IOSinkBase', () { + group('write()', () { + test("doesn't call add() for the empty string", () async { + var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); + sink.write(''); + }); + + test('converts the text to data and passes it to add', () async { + var sink = _IOSink(onAdd: expectAsync1((data) { + expect(data, equals(utf8.encode('hello'))); + })); + sink.write('hello'); + }); + + test('calls Object.toString()', () async { + var sink = _IOSink(onAdd: expectAsync1((data) { + expect(data, equals(utf8.encode('123'))); + })); + sink.write(123); + }); + + test('respects the encoding', () async { + var sink = _IOSink( + onAdd: expectAsync1((data) { + expect(data, equals(latin1.encode('Æ'))); + }), + encoding: latin1); + sink.write('Æ'); + }); + + test('throws if the sink is closed', () async { + var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.write('hello'), throwsStateError); + }); + }); + + group('writeAll()', () { + test('writes nothing for an empty iterable', () async { + var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); + sink.writeAll([]); + }); + + test('writes each object in the iterable', () async { + var chunks = >[]; + var sink = _IOSink( + onAdd: expectAsync1((data) { + chunks.add(data); + }, count: 3)); + + sink.writeAll(['hello', null, 123]); + expect(chunks, equals(['hello', 'null', '123'].map(utf8.encode))); + }); + + test('writes separators between each object', () async { + var chunks = >[]; + var sink = _IOSink( + onAdd: expectAsync1((data) { + chunks.add(data); + }, count: 5)); + + sink.writeAll(['hello', null, 123], '/'); + expect(chunks, + equals(['hello', '/', 'null', '/', '123'].map(utf8.encode))); + }); + + test('throws if the sink is closed', () async { + var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.writeAll(['hello']), throwsStateError); + }); + }); + + group('writeln()', () { + test('only writes a newline by default', () async { + var sink = _IOSink( + onAdd: expectAsync1((data) { + expect(data, equals(utf8.encode('\n'))); + }, count: 1)); + sink.writeln(); + }); + + test('writes the object followed by a newline', () async { + var chunks = >[]; + var sink = _IOSink( + onAdd: expectAsync1((data) { + chunks.add(data); + }, count: 2)); + sink.writeln(123); + + expect(chunks, equals(['123', '\n'].map(utf8.encode))); + }); + + test('throws if the sink is closed', () async { + var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.writeln(), throwsStateError); + }); + }); + + group('writeCharCode()', () { + test('writes the character code', () async { + var sink = _IOSink(onAdd: expectAsync1((data) { + expect(data, equals(utf8.encode('A'))); + })); + sink.writeCharCode($A); + }); + + test('respects the encoding', () async { + var sink = _IOSink( + onAdd: expectAsync1((data) { + expect(data, equals(latin1.encode('Æ'))); + }), + encoding: latin1); + sink.writeCharCode('Æ'.runes.first); + }); + + test('throws if the sink is closed', () async { + var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); + expect(sink.close(), completes); + expect(() => sink.writeCharCode($A), throwsStateError); + }); + }); + + group('flush()', () { + test('returns a future that completes when onFlush() is done', () async { + var completer = Completer(); + var sink = _IOSink(onFlush: expectAsync0(() => completer.future)); + + var flushDone = false; + sink.flush().then((_) => flushDone = true); + + await pumpEventQueue(); + expect(flushDone, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(flushDone, isTrue); + }); + + test('does nothing after close() is called', () { + var sink = + _IOSink(onFlush: expectAsync0(() => Future.value(), count: 0)); + expect(sink.close(), completes); + expect(sink.flush(), completes); + }); + + test("can't be called during addStream()", () { + var sink = + _IOSink(onFlush: expectAsync0(() => Future.value(), count: 0)); + sink.addStream(StreamController>().stream); + expect(() => sink.flush(), throwsStateError); + }); + + test('locks the sink as though a stream was being added', () { + var sink = _IOSink(onFlush: expectAsync0(() => Completer().future)); + sink.flush(); + expect(() => sink.add([0]), throwsStateError); + expect(() => sink.addError('oh no'), throwsStateError); + expect(() => sink.addStream(Stream.empty()), throwsStateError); + expect(() => sink.flush(), throwsStateError); + expect(() => sink.close(), throwsStateError); + }); + }); + }); +} + +/// A subclass of [StreamSinkBase] that takes all the overridable methods as +/// callbacks, for ease of testing. +class _StreamSink extends StreamSinkBase { + final void Function(int value) _onAdd; + final void Function(Object error, [StackTrace? stackTrace]) _onError; + final FutureOr Function() _onClose; + + _StreamSink( + {void Function(int value)? onAdd, + void Function(Object error, [StackTrace? stackTrace])? onError, + FutureOr Function()? onClose}) + : _onAdd = onAdd ?? ((_) {}), + _onError = onError ?? ((_, [__]) {}), + _onClose = onClose ?? (() {}); + + @override + void onAdd(int value) { + _onAdd(value); + } + + @override + void onError(Object error, [StackTrace? stackTrace]) { + _onError(error, stackTrace); + } + + @override + FutureOr onClose() => _onClose(); +} + +/// A subclass of [IOSinkBase] that takes all the overridable methods as +/// callbacks, for ease of testing. +class _IOSink extends IOSinkBase { + final void Function(List value) _onAdd; + final void Function(Object error, [StackTrace? stackTrace]) _onError; + final FutureOr Function() _onClose; + final Future Function() _onFlush; + + _IOSink( + {void Function(List value)? onAdd, + void Function(Object error, [StackTrace? stackTrace])? onError, + FutureOr Function()? onClose, + Future Function()? onFlush, + Encoding encoding = utf8}) + : _onAdd = onAdd ?? ((_) {}), + _onError = onError ?? ((_, [__]) {}), + _onClose = onClose ?? (() {}), + _onFlush = onFlush ?? (() => Future.value()), + super(encoding); + + @override + void onAdd(List value) { + _onAdd(value); + } + + @override + void onError(Object error, [StackTrace? stackTrace]) { + _onError(error, stackTrace); + } + + @override + FutureOr onClose() => _onClose(); + + @override + Future onFlush() => _onFlush(); +} From 0aa87e7afbda15ec07967685633855a57e5d12de Mon Sep 17 00:00:00 2001 From: Do Tuan Anh Date: Wed, 21 Jul 2021 02:27:55 +0700 Subject: [PATCH 200/260] Avoid missing streams when relistening to broadcast StreamGroup (dart-lang/async#184) In a broadcast `StreamGroup` when all listeners have canceled the inner subscriptions are kept only for single subscriber streams. When a new listener is added and subscriptions need to be recreated for inner broadcast streams the presence of a single subscriber subscription in the collection of subscriptions would cause the listen to bail out early and fail to subscribe to later broadcast streams. Change `return` to `continue` to keep going with the rest of the streams. --- pkgs/async/CHANGELOG.md | 3 +++ pkgs/async/lib/src/stream_group.dart | 2 +- pkgs/async/test/stream_group_test.dart | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 102ad59f..2c5a3ebb 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -4,6 +4,9 @@ easier to implement custom sinks. * Improve performance for `ChunkedStreamReader` by creating fewer internal sublists and specializing to create views for `Uint8List` chunks. +* Don't ignore broadcast streams added to a `StreamGroup` that doesn't have an + active listener but previously had listeners and contains a single + subscription inner stream. ## 2.7.0 diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index f7819bfd..c8414f49 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -192,7 +192,7 @@ class StreamGroup implements Sink> { // If this is a broadcast group and this isn't the first time it's been // listened to, there may still be some subscriptions to // single-subscription streams. - if (entry.value != null) return; + if (entry.value != null) continue; var stream = entry.key; try { diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 11c66c1e..256056e6 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -417,6 +417,24 @@ void main() { expect(controller.hasListener, isTrue); }); + test( + 'listens on streams that follow single-subscription streams when ' + 'relistening after a cancel', () async { + var controller1 = StreamController(); + streamGroup.add(controller1.stream); + streamGroup.stream.listen(null).cancel(); + + var controller2 = StreamController(); + streamGroup.add(controller2.stream); + + var emitted = []; + streamGroup.stream.listen(emitted.add); + controller1.add('one'); + controller2.add('two'); + await flushMicrotasks(); + expect(emitted, ['one', 'two']); + }); + test('never cancels single-subscription streams', () async { var subscription = streamGroup.stream.listen(null); From 83e71b49c45b57134a5627021d2ee6040ed8d53f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 22 Jul 2021 14:12:22 -0700 Subject: [PATCH 201/260] Prepare to publish (again) (dart-lang/async#190) I made the previous publish at a commit too early. Move the changelog for the `StreamGroup` bug fix to a new version and prepare to publish. --- pkgs/async/CHANGELOG.md | 9 ++++++--- pkgs/async/pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 2c5a3ebb..67d7be76 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,12 +1,15 @@ +## 2.8.1 + +* Don't ignore broadcast streams added to a `StreamGroup` that doesn't have an + active listener but previously had listeners and contains a single + subscription inner stream. + ## 2.8.0 * Add `EventSinkBase`, `StreamSinkBase`, and `IOSinkBase` classes to make it easier to implement custom sinks. * Improve performance for `ChunkedStreamReader` by creating fewer internal sublists and specializing to create views for `Uint8List` chunks. -* Don't ignore broadcast streams added to a `StreamGroup` that doesn't have an - active listener but previously had listeners and contains a single - subscription inner stream. ## 2.7.0 diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b0a68a67..8529db4f 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.8.0 +version: 2.8.1 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async From 3ff68969ae49f162ea94db7dc62d125e52d11f0c Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 6 Aug 2021 21:36:18 +0200 Subject: [PATCH 202/260] Deprecate the sink_base classes. (dart-lang/async#193) The approach was not considered a good fit for the package API, and we'll remove the classes and consider another approach, if one is needed. --- pkgs/async/CHANGELOG.md | 4 ++++ pkgs/async/lib/src/sink_base.dart | 3 +++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/io_sink_impl.dart | 3 +++ pkgs/async/test/sink_base_test.dart | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 67d7be76..e32ec99b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.8.2 + +* Deprecate `EventSinkBase`, `StreamSinkBase`, `IOSinkBase`. + ## 2.8.1 * Don't ignore broadcast streams added to a `StreamGroup` that doesn't have an diff --git a/pkgs/async/lib/src/sink_base.dart b/pkgs/async/lib/src/sink_base.dart index 873ce823..62ca9810 100644 --- a/pkgs/async/lib/src/sink_base.dart +++ b/pkgs/async/lib/src/sink_base.dart @@ -14,6 +14,7 @@ import 'async_memoizer.dart'; /// /// This takes care of ensuring that events can't be added after [close] is /// called. +@Deprecated('Will be removed in the next major release') abstract class EventSinkBase implements EventSink { /// Whether [close] has been called and no more events may be written. bool get _closed => _closeMemo.hasRun; @@ -61,6 +62,7 @@ abstract class EventSinkBase implements EventSink { /// /// This takes care of ensuring that events can't be added after [close] is /// called or during a call to [onStream]. +@Deprecated('Will be removed in the next major release') abstract class StreamSinkBase extends EventSinkBase implements StreamSink { /// Whether a call to [addStream] is ongoing. @@ -104,6 +106,7 @@ abstract class StreamSinkBase extends EventSinkBase /// /// This takes care of ensuring that events can't be added after [close] is /// called or during a call to [onStream]. +@Deprecated('Will be removed in the next major release') abstract class IOSinkBase extends StreamSinkBase> { /// See [IOSink.encoding] from `dart:io`. Encoding encoding; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 8529db4f..abfc12ca 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.8.1 +version: 2.8.2 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/io_sink_impl.dart b/pkgs/async/test/io_sink_impl.dart index 63fa289d..bd761b9a 100644 --- a/pkgs/async/test/io_sink_impl.dart +++ b/pkgs/async/test/io_sink_impl.dart @@ -2,6 +2,9 @@ // 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. +@deprecated +library io_sink_impl; + import 'dart:io'; import 'package:async/async.dart'; diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart index da3ca9fb..8ecf8c36 100644 --- a/pkgs/async/test/sink_base_test.dart +++ b/pkgs/async/test/sink_base_test.dart @@ -2,6 +2,9 @@ // 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. +@deprecated +library sink_base_test; + import 'dart:async'; import 'dart:convert'; From e02f3d89188ca03a7c5a0675cd99ecb1b88c796f Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 10 Aug 2021 16:26:09 +0200 Subject: [PATCH 203/260] Stop depending on pkg:charcode and pkg:pedantic. (dart-lang/async#191) Stop depending on pkg:charcode and pkg:pedantic. Use pkg:lints instead of pkg:pedantic (and update code as necessary). Use a local constant instead of depending on pkg:charcode. Remove unnecessary clutter in analysis_options.yaml, but do retain `analyzer`/`strong-mode`/`implicit-casts: false` --- pkgs/async/analysis_options.yaml | 10 +--------- pkgs/async/lib/src/chunked_stream_reader.dart | 4 ++-- pkgs/async/lib/src/stream_group.dart | 4 ++-- pkgs/async/lib/src/stream_sink_transformer.dart | 2 +- .../lib/src/stream_sink_transformer/reject_errors.dart | 5 ++++- pkgs/async/pubspec.yaml | 5 ++--- pkgs/async/test/io_sink_impl.dart | 2 +- pkgs/async/test/result/result_captureAll_test.dart | 2 ++ pkgs/async/test/result/result_flattenAll_test.dart | 2 ++ pkgs/async/test/sink_base_test.dart | 10 +++++----- pkgs/async/test/stream_queue_test.dart | 2 +- pkgs/async/test/subscription_transformer_test.dart | 5 +++-- 12 files changed, 26 insertions(+), 27 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index b58ef994..5c781356 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,15 +1,7 @@ -include: package:pedantic/analysis_options.yaml +include: package:lints/recommended.yaml analyzer: strong-mode: implicit-casts: false errors: todo: ignore - # Lint provided by pkg:pedantic – should fix this! - unawaited_futures: ignore - enable-experiment: - - non-nullable - -linter: - rules: - - prefer_typing_uninitialized_variables diff --git a/pkgs/async/lib/src/chunked_stream_reader.dart b/pkgs/async/lib/src/chunked_stream_reader.dart index a8963411..fd9be921 100644 --- a/pkgs/async/lib/src/chunked_stream_reader.dart +++ b/pkgs/async/lib/src/chunked_stream_reader.dart @@ -118,7 +118,7 @@ class ChunkedStreamReader { } _reading = true; - final substream = () async* { + Stream> substream() async* { // While we have data to read while (size > 0) { // Read something into the buffer, if buffer has been consumed. @@ -158,7 +158,7 @@ class ChunkedStreamReader { yield output; } } - }; + } final c = StreamController>(); c.onListen = () => c.addStream(substream()).whenComplete(c.close); diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index c8414f49..51e43b49 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -74,7 +74,7 @@ class StreamGroup implements Sink> { Stream get onIdle => (_onIdleController ??= StreamController.broadcast()).stream; - StreamController? _onIdleController; + StreamController? _onIdleController; /// Streams that have been added to the group, and their subscriptions if they /// have been subscribed to. @@ -169,7 +169,7 @@ class StreamGroup implements Sink> { /// [StreamSubscription.cancel]'s return value. Otherwise, it returns `null`. Future? remove(Stream stream) { var subscription = _subscriptions.remove(stream); - var future = subscription == null ? null : subscription.cancel(); + var future = subscription?.cancel(); if (_subscriptions.isEmpty) { _onIdleController?.add(null); diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index de48d39f..d42d19ce 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -53,7 +53,7 @@ abstract class StreamSinkTransformer { /// This means that calls to [StreamSink.add] on the returned sink may throw a /// [TypeError] if the argument type doesn't match the reified type of the /// sink. - @deprecated + @Deprecated("Will be removed in future version") // TODO remove TypeSafeStreamSinkTransformer static StreamSinkTransformer typed( StreamSinkTransformer transformer) => diff --git a/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart b/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart index a8d130f8..7e15d265 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart @@ -112,7 +112,10 @@ class RejectErrorsSink implements StreamSink { if (_closed) return done; _closed = true; - if (!_canceled) _doneCompleter.complete(_inner.close()); + if (!_canceled) { + // ignore: void_checks + _doneCompleter.complete(_inner.close()); + } return done; } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index abfc12ca..c636de6a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.8.2 +version: 2.8.3 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async @@ -12,8 +12,7 @@ dependencies: meta: ^1.1.7 dev_dependencies: - charcode: ^1.3.0 fake_async: ^1.2.0 - pedantic: ^1.10.0 + lints: ^1.0.0 stack_trace: ^1.10.0 test: ^1.16.0 diff --git a/pkgs/async/test/io_sink_impl.dart b/pkgs/async/test/io_sink_impl.dart index bd761b9a..832eb715 100644 --- a/pkgs/async/test/io_sink_impl.dart +++ b/pkgs/async/test/io_sink_impl.dart @@ -2,7 +2,7 @@ // 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. -@deprecated +@Deprecated("Tests deprecated functionality") library io_sink_impl; import 'dart:io'; diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index c00395a5..fb528360 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -2,6 +2,8 @@ // 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. +// ignore_for_file: file_names + import 'dart:async'; import 'dart:math' show Random; diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart index b87fec46..2521e9e2 100644 --- a/pkgs/async/test/result/result_flattenAll_test.dart +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -2,6 +2,8 @@ // 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. +// ignore_for_file: file_names + import 'package:async/async.dart'; import 'package:test/test.dart'; diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart index 8ecf8c36..33958dd0 100644 --- a/pkgs/async/test/sink_base_test.dart +++ b/pkgs/async/test/sink_base_test.dart @@ -2,17 +2,18 @@ // 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. -@deprecated +@Deprecated("Tests deprecated functionality") library sink_base_test; import 'dart:async'; import 'dart:convert'; -import 'package:charcode/charcode.dart'; import 'package:test/test.dart'; import 'package:async/async.dart'; +const int letterA = 0x41; + void main() { // We don't explicitly test [EventSinkBase] because it shares all the relevant // implementation with [StreamSinkBase]. @@ -54,7 +55,6 @@ void main() { var controller = StreamController(); var addStreamCompleted = false; sink.addStream(controller.stream).then((_) => addStreamCompleted = true); - ; await pumpEventQueue(); expect(addStreamCompleted, isFalse); @@ -277,7 +277,7 @@ void main() { var sink = _IOSink(onAdd: expectAsync1((data) { expect(data, equals(utf8.encode('A'))); })); - sink.writeCharCode($A); + sink.writeCharCode(letterA); }); test('respects the encoding', () async { @@ -292,7 +292,7 @@ void main() { test('throws if the sink is closed', () async { var sink = _IOSink(onAdd: expectAsync1((_) {}, count: 0)); expect(sink.close(), completes); - expect(() => sink.writeCharCode($A), throwsStateError); + expect(() => sink.writeCharCode(letterA), throwsStateError); }); }); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 74c91e66..1d45a8c2 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -543,7 +543,7 @@ void main() { test('returns the result of closing the underlying subscription', () async { var controller = - StreamController(onCancel: () => Future.value(42)); + StreamController(onCancel: () => Future.value(42)); var events = StreamQueue(controller.stream); expect(await events.cancel(immediate: true), 42); }); diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index 8278ea0c..a95c7c46 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -13,8 +13,9 @@ void main() { group('with no callbacks', () { test('forwards cancellation', () async { var isCanceled = false; - var cancelCompleter = Completer(); - var controller = StreamController(onCancel: expectAsync0(() { + var cancelCompleter = Completer(); + var controller = + StreamController(onCancel: expectAsync0>(() { isCanceled = true; return cancelCompleter.future; })); From f7852143fbdd3c0f88b98dcbe8cbff2caab37721 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 31 Aug 2021 16:07:57 -0700 Subject: [PATCH 204/260] Add StreamExtensions.firstOrNull (dart-lang/async#195) By analogy to IterableExtensions.firstOrNull in the collection package. --- pkgs/async/CHANGELOG.md | 4 +++ pkgs/async/lib/src/stream_extensions.dart | 25 ++++++++++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_extensions_test.dart | 37 +++++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index e32ec99b..9acccf91 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.9.0 + +* Add `StreamExtensions.firstOrNull`. + ## 2.8.2 * Deprecate `EventSinkBase`, `StreamSinkBase`, `IOSinkBase`. diff --git a/pkgs/async/lib/src/stream_extensions.dart b/pkgs/async/lib/src/stream_extensions.dart index 3801a025..129ce26f 100644 --- a/pkgs/async/lib/src/stream_extensions.dart +++ b/pkgs/async/lib/src/stream_extensions.dart @@ -33,4 +33,29 @@ extension StreamExtensions on Stream { sink.close(); })); } + + /// A future which completes with the first event of this stream, or with + /// `null`. + /// + /// This stream is listened to, and if it emits any event, whether a data + /// event or an error event, the future completes with the same data value or + /// error. If the stream ends without emitting any events, the future is + /// completed with `null`. + Future get firstOrNull { + var completer = Completer.sync(); + final subscription = listen(null); + subscription + ..onData((event) { + subscription.cancel(); + completer.complete(event); + }) + ..onError((Object error, StackTrace stackTrace) { + subscription.cancel(); + completer.completeError(error, stackTrace); + }) + ..onDone(() { + completer.complete(null); + }); + return completer.future; + } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index c636de6a..ec3613a5 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.8.3 +version: 2.9.0-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/stream_extensions_test.dart b/pkgs/async/test/stream_extensions_test.dart index 85a3cee2..2118ae75 100644 --- a/pkgs/async/test/stream_extensions_test.dart +++ b/pkgs/async/test/stream_extensions_test.dart @@ -55,4 +55,41 @@ void main() { expect(() => Stream.fromIterable([1]).slices(0), throwsRangeError); }); }); + + group('.firstOrNull', () { + test('returns the first data event', () { + expect( + Stream.fromIterable([1, 2, 3, 4]).firstOrNull, completion(equals(1))); + }); + + test('returns the first error event', () { + expect(Stream.error('oh no').firstOrNull, throwsA('oh no')); + }); + + test('returns null for an empty stream', () { + expect(Stream.empty().firstOrNull, completion(isNull)); + }); + + test('cancels the subscription after an event', () async { + var isCancelled = false; + var controller = StreamController(onCancel: () { + isCancelled = true; + }); + controller.add(1); + + await expectLater(controller.stream.firstOrNull, completion(equals(1))); + expect(isCancelled, isTrue); + }); + + test('cancels the subscription after an error', () async { + var isCancelled = false; + var controller = StreamController(onCancel: () { + isCancelled = true; + }); + controller.addError('oh no'); + + await expectLater(controller.stream.firstOrNull, throwsA('oh no')); + expect(isCancelled, isTrue); + }); + }); } From cbce1bc273e3499d5c5d05076579cdce84a372c2 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 1 Sep 2021 17:27:55 -0700 Subject: [PATCH 205/260] Add additional CancelableOperation utilities (dart-lang/async#194) * Add CancelableOperation.fromSubscription * Add CancelableOperation.race() --- pkgs/async/CHANGELOG.md | 4 + pkgs/async/lib/src/cancelable_operation.dart | 56 +++++++++ .../async/test/cancelable_operation_test.dart | 116 ++++++++++++++++++ 3 files changed, 176 insertions(+) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 9acccf91..815c523e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -2,6 +2,10 @@ * Add `StreamExtensions.firstOrNull`. +* Add a `CancelableOperation.fromSubscription()` static factory. + +* Add a `CancelableOperation.race()` static method. + ## 2.8.2 * Deprecate `EventSinkBase`, `StreamSinkBase`, `IOSinkBase`. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index e04af905..f03f6729 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -39,6 +39,62 @@ class CancelableOperation { return completer.operation; } + /// Creates a [CancelableOperation] wrapping [subscription]. + /// + /// This overrides [subscription.onDone] and [subscription.onError] so that + /// the returned operation will complete when the subscription completes or + /// emits an error. When this operation is canceled or when it emits an error, + /// the subscription will be canceled (unlike + /// `CancelableOperation.fromFuture(subscription.asFuture())`). + static CancelableOperation fromSubscription( + StreamSubscription subscription) { + var completer = CancelableCompleter(onCancel: subscription.cancel); + subscription.onDone(completer.complete); + subscription.onError((Object error, StackTrace stackTrace) { + subscription.cancel().whenComplete(() { + completer.completeError(error, stackTrace); + }); + }); + return completer.operation; + } + + /// Returns a [CancelableOperation] that completes with the value of the first + /// of [operations] to complete. + /// + /// Once any of [operations] completes, its result is forwarded to the + /// returned [CancelableOperation] and the rest are cancelled. When the + /// returned operation is cancelled, all the [operations] are cancelled as + /// well. + static CancelableOperation race( + Iterable> operations) { + operations = operations.toList(); + if (operations.isEmpty) { + throw ArgumentError.value("May not be empty", "operations"); + } + + var done = false; + // Note: if one of the completers has already completed, it's not actually + // cancelled by this. + Future _cancelAll() { + done = true; + return Future.wait(operations.map((operation) => operation.cancel())); + } + + var completer = CancelableCompleter(onCancel: _cancelAll); + for (var operation in operations) { + operation.then((value) { + if (!done) completer.complete(_cancelAll().then((_) => value)); + }, onError: (error, stackTrace) { + if (!done) { + completer.complete( + _cancelAll().then((_) => Future.error(error, stackTrace))); + } + }); + } + + return completer.operation; + } + /// The value returned by the operation. Future get value => _completer._inner.future; diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index a46dfe2b..fa41001d 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -122,6 +122,30 @@ void main() { expect(operation.value, throwsA('error')); }); }); + + group('CancelableOperation.fromSubscription', () { + test('forwards a done event once it completes', () async { + var controller = StreamController(); + var operationCompleted = false; + CancelableOperation.fromSubscription(controller.stream.listen(null)) + .then((_) { + operationCompleted = true; + }); + + await flushMicrotasks(); + expect(operationCompleted, isFalse); + + controller.close(); + await flushMicrotasks(); + expect(operationCompleted, isTrue); + }); + + test('forwards errors', () { + var operation = CancelableOperation.fromSubscription( + Stream.error('error').listen(null)); + expect(operation.value, throwsA('error')); + }); + }); }); group('when canceled', () { @@ -237,6 +261,37 @@ void main() { await flushMicrotasks(); expect(fired, isTrue); }); + + test('CancelableOperation.fromSubscription() cancels the subscription', + () async { + var cancelCompleter = Completer(); + var canceled = false; + var controller = StreamController(onCancel: () { + canceled = true; + return cancelCompleter.future; + }); + var operation = + CancelableOperation.fromSubscription(controller.stream.listen(null)); + + await flushMicrotasks(); + expect(canceled, isFalse); + + // The `cancel()` call shouldn't complete until + // `StreamSubscription.cancel` completes. + var cancelCompleted = false; + expect( + operation.cancel().then((_) { + cancelCompleted = true; + }), + completes); + await flushMicrotasks(); + expect(canceled, isTrue); + expect(cancelCompleted, isFalse); + + cancelCompleter.complete(); + await flushMicrotasks(); + expect(cancelCompleted, isTrue); + }); }); group('asStream()', () { @@ -440,4 +495,65 @@ void main() { }); }); }); + + group('race()', () { + late bool canceled1; + late CancelableCompleter completer1; + late bool canceled2; + late CancelableCompleter completer2; + late bool canceled3; + late CancelableCompleter completer3; + late CancelableOperation operation; + setUp(() { + canceled1 = false; + completer1 = CancelableCompleter(onCancel: () { + canceled1 = true; + }); + + canceled2 = false; + completer2 = CancelableCompleter(onCancel: () { + canceled2 = true; + }); + + canceled3 = false; + completer3 = CancelableCompleter(onCancel: () { + canceled3 = true; + }); + + operation = CancelableOperation.race( + [completer1.operation, completer2.operation, completer3.operation]); + }); + + test('returns the first value to complete', () { + completer1.complete(1); + completer2.complete(2); + completer3.complete(3); + + expect(operation.value, completion(equals(1))); + }); + + test('throws the first error to complete', () { + completer1.completeError("error 1"); + completer2.completeError("error 2"); + completer3.completeError("error 3"); + + expect(operation.value, throwsA("error 1")); + }); + + test('cancels any completers that haven\'t completed', () async { + completer1.complete(1); + await expectLater(operation.value, completion(equals(1))); + expect(canceled1, isFalse); + expect(canceled2, isTrue); + expect(canceled3, isTrue); + }); + + test('cancels all completers when the operation is completed', () async { + await operation.cancel(); + + expect(canceled1, isTrue); + expect(canceled2, isTrue); + expect(canceled3, isTrue); + }); + }); } From fe8ec380ea11aefdfe11c84f72fbcce07a2e8218 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 9 Sep 2021 17:20:13 -0700 Subject: [PATCH 206/260] Fix an ArgumentError constructor call (dart-lang/async#197) The arguments that were passed match the intention of the `ArgumentError` constructor, while the `ArgumentError.value` constructor reverses the message and argument name, and includes the argument value. Including the value is not useful in this case since it is always `[]` and in most cases won't be the exact instance that was passed because of the `.toList()` call which would usually construct a copy. Bump the min SDK constraint to `2.14` which is the first release allowing the `name` argument to the `ArgumentError` constructor. --- pkgs/async/.github/workflows/test-package.yml | 2 +- pkgs/async/lib/src/cancelable_operation.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index e47bf660..fdddad8b 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.14.1, dev] steps: - uses: actions/checkout@v2 - uses: dart-lang/setup-dart@v1.0 diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index f03f6729..58684d68 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -69,7 +69,7 @@ class CancelableOperation { Iterable> operations) { operations = operations.toList(); if (operations.isEmpty) { - throw ArgumentError.value("May not be empty", "operations"); + throw ArgumentError("May not be empty", "operations"); } var done = false; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index ec3613a5..10d7d8fb 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -5,7 +5,7 @@ description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.14.0 <3.0.0" dependencies: collection: ^1.15.0 From e1ffd3d29f1f12a628ae12d27bcb9723b4642393 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Tue, 14 Sep 2021 18:08:49 +0200 Subject: [PATCH 207/260] Let `firstOrNull` extension method wait for `cancel` to complete. (dart-lang/async#196) --- pkgs/async/lib/src/stream_extensions.dart | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/pkgs/async/lib/src/stream_extensions.dart b/pkgs/async/lib/src/stream_extensions.dart index 129ce26f..f8a180f5 100644 --- a/pkgs/async/lib/src/stream_extensions.dart +++ b/pkgs/async/lib/src/stream_extensions.dart @@ -43,19 +43,15 @@ extension StreamExtensions on Stream { /// completed with `null`. Future get firstOrNull { var completer = Completer.sync(); - final subscription = listen(null); - subscription - ..onData((event) { - subscription.cancel(); + final subscription = listen(null, + onError: completer.completeError, + onDone: completer.complete, + cancelOnError: true); + subscription.onData((event) { + subscription.cancel().whenComplete(() { completer.complete(event); - }) - ..onError((Object error, StackTrace stackTrace) { - subscription.cancel(); - completer.completeError(error, stackTrace); - }) - ..onDone(() { - completer.complete(null); }); + }); return completer.future; } } From a58f0e77372e9f35f81fe123d7adbea4d2b3fe99 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Mon, 4 Oct 2021 07:45:59 -0700 Subject: [PATCH 208/260] update some StreamController apis to Future from Future (dart-lang/async#198) This is probably technically breaking for some cases - but it doesn't look to be breaking for anything at all internally, so I think in practice it is not breaking. Finishing up a global presubmit now with all the changes (but already did one with a subset). WDYT? --- pkgs/async/CHANGELOG.md | 5 ++++- pkgs/async/lib/src/stream_group.dart | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 815c523e..aabfb367 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.9.0 +## 2.9.0-dev * Add `StreamExtensions.firstOrNull`. @@ -6,6 +6,9 @@ * Add a `CancelableOperation.race()` static method. +* Update `StreamGroup` methods that return a `Future` today to return + a `Future` instead. + ## 2.8.2 * Deprecate `EventSinkBase`, `StreamSinkBase`, `IOSinkBase`. diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 51e43b49..514c3a4a 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -137,7 +137,7 @@ class StreamGroup implements Sink> { /// /// Throws a [StateError] if this group is closed. @override - Future? add(Stream stream) { + Future? add(Stream stream) { if (_closed) { throw StateError("Can't add a Stream to a closed StreamGroup."); } @@ -167,7 +167,7 @@ class StreamGroup implements Sink> { /// /// If [stream]'s subscription is canceled, this returns /// [StreamSubscription.cancel]'s return value. Otherwise, it returns `null`. - Future? remove(Stream stream) { + Future? remove(Stream stream) { var subscription = _subscriptions.remove(stream); var future = subscription?.cancel(); @@ -226,7 +226,7 @@ class StreamGroup implements Sink> { /// A callback called when [stream] is canceled. /// /// This is only called for single-subscription groups. - Future? _onCancel() { + Future? _onCancel() { _state = _StreamGroupState.canceled; var futures = _subscriptions.entries @@ -287,7 +287,7 @@ class StreamGroup implements Sink> { /// /// Returns a [Future] that completes once [stream] has actually been closed. @override - Future close() { + Future close() { if (_closed) return _controller.done; _closed = true; From 48da943201b5741e5a8e88139695ac0504985410 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 9 Mar 2022 10:50:42 +0100 Subject: [PATCH 209/260] Update test to not assume completion happens synchronously. (dart-lang/async#203) --- pkgs/async/test/cancelable_operation_test.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index fa41001d..cf2fd8b9 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -421,12 +421,12 @@ void main() { }); group('original operation canceled', () { - test('onCancel not set', () { + test('onCancel not set', () async { onCancel = null; final operation = runThen(); - expect(originalCompleter.operation.cancel(), completes); + await expectLater(originalCompleter.operation.cancel(), completes); expect(operation.isCanceled, true); }); @@ -460,8 +460,10 @@ void main() { var operation = runThen(); var workCompleter = Completer(); originalCompleter.complete(workCompleter.future); - originalCompleter.operation.cancel(); + var cancelation = originalCompleter.operation.cancel(); + expect(originalCompleter.isCanceled, true); workCompleter.complete(0); + await cancelation; expect(operation.isCanceled, true); await workCompleter.future; }); From ea542a7f0e5f371534e3cb85160bd42f801e0770 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 9 Mar 2022 17:35:03 +0100 Subject: [PATCH 210/260] Clean up `CancelableOperation`. (dart-lang/async#204) * Clean up `CancelableOperation`. Some clean-ups and simplifications of the code. Should not change any behavior, but differences in future chaining can potentially change timing. But don't change `Future` to `Future` today. --- pkgs/async/lib/src/cancelable_operation.dart | 198 ++++++++++--------- pkgs/async/lib/src/lazy_stream.dart | 3 +- pkgs/async/lib/src/utils.dart | 12 -- 3 files changed, 109 insertions(+), 104 deletions(-) delete mode 100644 pkgs/async/lib/src/utils.dart diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 58684d68..95332dcf 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -4,10 +4,6 @@ import 'dart:async'; -import 'package:async/async.dart'; - -import 'utils.dart'; - /// An asynchronous operation that can be cancelled. /// /// The value of this operation is exposed as [value]. When this operation is @@ -16,28 +12,25 @@ import 'utils.dart'; class CancelableOperation { /// The completer that produced this operation. /// - /// This is canceled when [cancel] is called. + /// That completer is canceled when [cancel] is called. final CancelableCompleter _completer; CancelableOperation._(this._completer); - /// Creates a [CancelableOperation] wrapping [inner]. + /// Creates a [CancelableOperation] with the same result as the [result] future. /// /// When this operation is canceled, [onCancel] will be called and any value - /// or error produced by [inner] will be discarded. If [onCancel] returns a - /// [Future], it will be forwarded to [cancel]. + /// or error later produced by [result] will be discarded. + /// If [onCancel] returns a [Future], it will be returned by [cancel]. /// - /// [onCancel] will be called synchronously when the operation is canceled. - /// It's guaranteed to only be called once. + /// The [onCancel] funcion will be called synchronously + /// when the new operation is canceled, and will be called at most once.\ /// - /// Calling this constructor is equivalent to creating a [CancelableCompleter] - /// and completing it with [inner]. - factory CancelableOperation.fromFuture(Future inner, - {FutureOr Function()? onCancel}) { - var completer = CancelableCompleter(onCancel: onCancel); - completer.complete(inner); - return completer.operation; - } + /// Calling this constructor is equivalent to creating a + /// [CancelableCompleter] and completing it with [result]. + factory CancelableOperation.fromFuture(Future result, + {FutureOr Function()? onCancel}) => + (CancelableCompleter(onCancel: onCancel)..complete(result)).operation; /// Creates a [CancelableOperation] wrapping [subscription]. /// @@ -58,12 +51,12 @@ class CancelableOperation { return completer.operation; } - /// Returns a [CancelableOperation] that completes with the value of the first + /// Creates a [CancelableOperation] that completes with the value of the first /// of [operations] to complete. /// /// Once any of [operations] completes, its result is forwarded to the - /// returned [CancelableOperation] and the rest are cancelled. When the - /// returned operation is cancelled, all the [operations] are cancelled as + /// new [CancelableOperation] and the rest are cancelled. If the + /// bew operation is cancelled, all the [operations] are cancelled as /// well. static CancelableOperation race( Iterable> operations) { @@ -73,8 +66,8 @@ class CancelableOperation { } var done = false; - // Note: if one of the completers has already completed, it's not actually - // cancelled by this. + // Note: if one or more of the completers have already completed, + // they're not actually cancelled by this. Future _cancelAll() { done = true; return Future.wait(operations.map((operation) => operation.cancel())); @@ -83,11 +76,11 @@ class CancelableOperation { var completer = CancelableCompleter(onCancel: _cancelAll); for (var operation in operations) { operation.then((value) { - if (!done) completer.complete(_cancelAll().then((_) => value)); + if (!done) _cancelAll().whenComplete(() => completer.complete(value)); }, onError: (error, stackTrace) { if (!done) { - completer.complete( - _cancelAll().then((_) => Future.error(error, stackTrace))); + _cancelAll() + .whenComplete(() => completer.completeError(error, stackTrace)); } }); } @@ -95,13 +88,17 @@ class CancelableOperation { return completer.operation; } - /// The value returned by the operation. + /// The result of this operation, if not cancelled. + /// + /// This future will not complete if the operation is cancelled. + /// Use [valueOrCancellation] for a future which completes + /// both if the operation is cancelled and if it isn't. Future get value => _completer._inner.future; /// Creates a [Stream] containing the result of this operation. /// /// This is like `value.asStream()`, but if a subscription to the stream is - /// canceled, this is as well. + /// canceled, this operation is as well. Stream asStream() { var controller = StreamController(sync: true, onCancel: _completer._cancel); @@ -124,28 +121,31 @@ class CancelableOperation { /// returned by [cancel], then completes to [cancellationValue]. Future valueOrCancellation([T? cancellationValue]) { var completer = Completer.sync(); - value.then((result) => completer.complete(result), - onError: completer.completeError); + value.then(completer.complete, onError: completer.completeError); - _completer._cancelMemo.future.then((_) { + _completer._cancelCompleter.future.then((_) { completer.complete(cancellationValue); }, onError: completer.completeError); return completer.future; } - /// Registers callbacks to be called when this operation completes. + /// Creates a new cancelable operation to be completed + /// when this operation completes or is cancelled. /// - /// [onValue] and [onError] behave in the same way as [Future.then]. + /// The [onValue] and [onError] callbacks behave in the same way as + /// for [Future.then], and the result of those callbacks is used to complete + /// the returned cancelable operation. /// - /// If [onCancel] is provided, and this operation is canceled, the [onCancel] - /// callback is called and the returned operation completes with the result. + /// If [onCancel] is provided, and the this operation is canceled, + /// the [onCancel] callback is called and the returned operation completes + /// with the result returned by that call. /// - /// If [onCancel] is not given, and this operation is canceled, then the - /// returned operation is canceled. + /// If [onCancel] is not provided, and this operation is canceled, then the + /// returned operation is also canceled. /// - /// If [propagateCancel] is `true` and the returned operation is canceled then - /// this operation is canceled. The default is `false`. + /// If [propagateCancel] is `true` and the returned operation is canceled + /// then this operation is canceled. The default is `false`. CancelableOperation then(FutureOr Function(T) onValue, {FutureOr Function(Object, StackTrace)? onError, FutureOr Function()? onCancel, @@ -153,26 +153,19 @@ class CancelableOperation { final completer = CancelableCompleter(onCancel: propagateCancel ? cancel : null); - valueOrCancellation().then((T? result) { - if (!completer.isCanceled) { - if (isCompleted && !isCanceled) { - assert(result is T); - completer.complete(Future.sync(() => onValue(result as T))); - } else if (onCancel != null) { - completer.complete(Future.sync(onCancel)); - } else { - completer._cancel(); - } - } - }, onError: (Object error, StackTrace stackTrace) { - if (!completer.isCanceled) { - if (onError != null) { - completer.complete(Future.sync(() => onError(error, stackTrace))); - } else { - completer.completeError(error, stackTrace); - } + if (!isCanceled) { + value + .then(onValue, onError: onError) + .then(completer.complete, onError: completer.completeError); + } + _completer._cancelCompleter.future.then((_) { + if (onCancel != null) { + completer.complete(Future.sync(onCancel)); + } else { + completer._cancel(); } }); + return completer.operation; } @@ -196,24 +189,51 @@ class CancelableOperation { /// A completer for a [CancelableOperation]. class CancelableCompleter { /// The completer for the wrapped future. + /// + /// At most one of `_inner.future` and `_cancelCompleter.future` will + /// ever complete. final _inner = Completer(); - /// The callback to call if the future is canceled. - final FutureOrCallback? _onCancel; + /// Completed when `cancel` is called. + /// + /// At most one of `_inner.future` and `_cancelCompleter.future` will + /// ever complete. + final _cancelCompleter = Completer(); + + /// The callback to call if the operation is canceled. + final FutureOr Function()? _onCancel; + + /// The operation controlled by this completer. + late final operation = CancelableOperation._(this); + + /// Set when [complete] or [completeError] is called. + /// + /// Completing twice is not allowed. + /// + /// If [complete] is called with a future, it's still possible to + /// cancel the operation until that future completes, + /// so this value and [_isCanceled] are not mutually exclusive. + bool _isCompleted = false; + + /// Set when [cancel] is called. + /// + /// Cancelling twice does nothing, nor does completing after cancelling. + bool _isCanceled = false; /// Creates a new completer for a [CancelableOperation]. /// - /// When the future operation canceled, as long as the completer hasn't yet - /// completed, [onCancel] is called. If [onCancel] returns a [Future], it's - /// forwarded to [CancelableOperation.cancel]. + /// The cancelable [operation] can be completed using + /// [complete] or [completeError]. + /// + /// The [onCancel] function is called if the [operation] is canceled, + /// by calling [CancelableOperation.cancel] + /// before the operation has completed. + /// If [onCancel] returns a [Future], + /// that future is also returned by [CancelableOperation.cancel]. /// - /// [onCancel] will be called synchronously when the operation is canceled. - /// It's guaranteed to only be called once. + /// The [onCancel] function will be called at most once. CancelableCompleter({FutureOr Function()? onCancel}) : _onCancel = onCancel; - /// The operation controlled by this completer. - late final operation = CancelableOperation._(this); - /// Whether the [complete] or [completeError] have been called. /// /// Once this completer has been completed with either a result or error, @@ -223,20 +243,16 @@ class CancelableCompleter { /// completed before it's [operation] is completed. In that case the /// [operation] may still be canceled before the result is available. bool get isCompleted => _isCompleted; - bool _isCompleted = false; /// Whether the completer was canceled before the result was ready. bool get isCanceled => _isCanceled; - bool _isCanceled = false; - - /// The memoizer for [_cancel]. - final _cancelMemo = AsyncMemoizer(); - /// Completes [operation] to [value]. + /// Completes [operation] with [value]. /// - /// If [value] is a [Future] the [operation] will complete with the result of - /// that `Future` once it is available. - /// In that case [isComplete] will be true before the [operation] is complete. + /// If [value] is a [Future] the [operation] will complete + /// with the result of that `Future` once it is available. + /// In that case [isComplete] will be `true` before the [operation] + /// is complete. /// /// If the type [T] is not nullable [value] may be not be omitted or `null`. /// @@ -247,20 +263,19 @@ class CancelableCompleter { if (_isCompleted) throw StateError('Operation already completed'); _isCompleted = true; - if (value is! Future) { + if (value is! Future) { if (_isCanceled) return; _inner.complete(value); return; } - final future = value as Future; if (_isCanceled) { // Make sure errors from [value] aren't top-leveled. - future.catchError((_) {}); + value.ignore(); return; } - future.then((result) { + value.then((result) { if (_isCanceled) return; _inner.complete(result); }, onError: (Object error, StackTrace stackTrace) { @@ -282,24 +297,27 @@ class CancelableCompleter { _inner.completeError(error, stackTrace); } - /// Cancel the operation. + /// Cancels the operation. + /// + /// If the operation has already completed, prior to being cancelled, + /// this method does nothing. + /// If the operation has already been cancelled, this method returns + /// the same result as the first call to `_cancel`. /// - /// This call is be ignored if the result of the operation is already - /// available. /// The result of the operation may only be available some time after /// the completer has been completed (using [complete] or [completeError], /// which sets [isCompleted] to true) if completed with a [Future]. /// The completer can be cancelled until the result becomes available, /// even if [isCompleted] is true. - /// - /// This call is ignored if this completer has already been canceled. - Future _cancel() { - if (_inner.isCompleted) return Future.value(); + Future _cancel() { + if (_inner.isCompleted) return Future.value(null); - return _cancelMemo.runOnce(() { + if (!_isCanceled) { _isCanceled = true; var onCancel = _onCancel; - if (onCancel != null) return onCancel(); - }); + _cancelCompleter + .complete(onCancel == null ? null : Future.sync(onCancel)); + } + return _cancelCompleter.future; } } diff --git a/pkgs/async/lib/src/lazy_stream.dart b/pkgs/async/lib/src/lazy_stream.dart index e6779ee5..e0facaa5 100644 --- a/pkgs/async/lib/src/lazy_stream.dart +++ b/pkgs/async/lib/src/lazy_stream.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'stream_completer.dart'; -import 'utils.dart'; /// A [Stream] wrapper that forwards to another [Stream] that's initialized /// lazily. @@ -15,7 +14,7 @@ import 'utils.dart'; /// produce a `Stream`. class LazyStream extends Stream { /// The callback that's called to create the inner stream. - FutureOrCallback>? _callback; + FutureOr> Function()? _callback; /// Creates a single-subscription `Stream` that calls [callback] when it gets /// a listener and forwards to the returned stream. diff --git a/pkgs/async/lib/src/utils.dart b/pkgs/async/lib/src/utils.dart deleted file mode 100644 index 39e5a8a8..00000000 --- a/pkgs/async/lib/src/utils.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2016, 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. - -import 'dart:async'; - -/// A generic typedef for a function that takes one type and returns another. -typedef UnaryFunction = F Function(E argument); - -/// A typedef for a function that takes no arguments and returns a Future or a -/// value. -typedef FutureOrCallback = FutureOr Function(); From 6f3c61de4acb52d1ea05c268043770ba39116dde Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Thu, 10 Mar 2022 13:03:06 +0100 Subject: [PATCH 211/260] Avoid leaking unreachable listeners on `CancelableOperation`. (dart-lang/async#206) * Make `CancelableOperation` not hold onto unnecessary callbacks. The existing `CancelableOperation` contains two completers, where at most one will ever complete. Listeners added to the other completer's future will never be released as long as the operation is alive, even after the operation has otherwise completed. This change drops reference to the other completer when one completer is chosen for completion. That can allow the completer, its future, and that future's listeners to be GC'ed when it's known that they'll never be relevant again. The change has one visible effect: If asking for the `value` future after the operation has been cancelled and the value completer has been released, you'll get a new future, which also never completes, which is not identical to other futures returned by the same getter. THis only matters if someone checks the future for identity, their behavior is exactly the same (no behavior, whatsoever). Since the same class returns a completely new future on each call read of `valueOrCancellation`, I think that's a reasonable change, and unlikely to affect anyone. (Users are not usually expecting asynchronous functions to return the same future every time since `async` functions don't.) Fixes dart-lang/async#200. --- pkgs/async/lib/src/cancelable_operation.dart | 142 ++++++++++++------- 1 file changed, 94 insertions(+), 48 deletions(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 95332dcf..394ba07e 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -13,7 +13,7 @@ class CancelableOperation { /// The completer that produced this operation. /// /// That completer is canceled when [cancel] is called. - final CancelableCompleter _completer; + CancelableCompleter _completer; CancelableOperation._(this._completer); @@ -93,7 +93,7 @@ class CancelableOperation { /// This future will not complete if the operation is cancelled. /// Use [valueOrCancellation] for a future which completes /// both if the operation is cancelled and if it isn't. - Future get value => _completer._inner.future; + Future get value => _completer._inner?.future ?? Completer().future; /// Creates a [Stream] containing the result of this operation. /// @@ -103,7 +103,7 @@ class CancelableOperation { var controller = StreamController(sync: true, onCancel: _completer._cancel); - value.then((value) { + _completer._inner?.future.then((value) { controller.add(value); controller.close(); }, onError: (Object error, StackTrace stackTrace) { @@ -123,7 +123,7 @@ class CancelableOperation { var completer = Completer.sync(); value.then(completer.complete, onError: completer.completeError); - _completer._cancelCompleter.future.then((_) { + _completer._cancelCompleter?.future.then((_) { completer.complete(cancellationValue); }, onError: completer.completeError); @@ -153,12 +153,10 @@ class CancelableOperation { final completer = CancelableCompleter(onCancel: propagateCancel ? cancel : null); - if (!isCanceled) { - value - .then(onValue, onError: onError) - .then(completer.complete, onError: completer.completeError); - } - _completer._cancelCompleter.future.then((_) { + _completer._inner?.future + .then(onValue, onError: onError) + .then(completer.complete, onError: completer.completeError); + _completer._cancelCompleter?.future.then((_) { if (onCancel != null) { completer.complete(Future.sync(onCancel)); } else { @@ -176,49 +174,81 @@ class CancelableOperation { Future cancel() => _completer._cancel(); /// Whether this operation has been canceled before it completed. - bool get isCanceled => _completer.isCanceled; + bool get isCanceled => _completer._isCanceled; /// Whether the result of this operation is ready. /// /// When ready, the [value] future is completed with the result value /// or error, and this operation can no longer be cancelled. /// An operation may be complete before the listeners on [value] are invoked. - bool get isCompleted => _completer._inner.isCompleted; + bool get isCompleted => _completer._isCompleted; } /// A completer for a [CancelableOperation]. class CancelableCompleter { + // The cancelable completer is in one of the following states: + // * Initial: + // _inner != null + // _cancelCompleter != null + // _mayComplete: true + // + // * Async-completed: `complete` called with a future while Initial. + // _inner != null + // _cancelCompleter != null + // _mayComplete: false + // + // * Completed: `complete` called with a value or `completeError` called + // while Initial, or the future passed in Async-completed completes + // while AsyncCompleted. + // _inner != null + // _cancelCompleter == null + // _mayComplete: false + // + // * Cancelled may-complete: `_cancel` called while Initial. + // Allows calling `complete`/`completeError` even if it does nothing. + // _inner == null + // _cancelCompleter != null + // _mayComplete: true + // + // * Cancelled can't-complete: `_cancel` called while Async-completed. + // _inner == null + // _cancelCompleter != null + // _mayComplete: false + /// The completer for the wrapped future. /// /// At most one of `_inner.future` and `_cancelCompleter.future` will /// ever complete. - final _inner = Completer(); + /// Set to `null` when when the operation is canceled, because then + /// it's guaranteed that this completer will never complete. + Completer? _inner = Completer(); /// Completed when `cancel` is called. /// /// At most one of `_inner.future` and `_cancelCompleter.future` will /// ever complete. - final _cancelCompleter = Completer(); + /// Set to `null` when [_inner] is completed, because then it's + /// guaranteed that this completer will never complete. + Completer? _cancelCompleter = Completer(); /// The callback to call if the operation is canceled. final FutureOr Function()? _onCancel; - /// The operation controlled by this completer. - late final operation = CancelableOperation._(this); - - /// Set when [complete] or [completeError] is called. + /// Whether [complete] or [completeError] may still be called. /// - /// Completing twice is not allowed. + /// Set to false when calling either. /// - /// If [complete] is called with a future, it's still possible to - /// cancel the operation until that future completes, - /// so this value and [_isCanceled] are not mutually exclusive. - bool _isCompleted = false; + /// When completing by calling [complete] with a future, + /// it's still possible to cancel until the result is actually + /// available. + /// You are also allowed to call [complete] or [completeError] + /// after the operation has been canceled, as long as you only call it once. + /// It just won't do anything after the operation is cancelled. + /// This value only guards the calls to [complete] and [completeError]. + bool _mayComplete = true; - /// Set when [cancel] is called. - /// - /// Cancelling twice does nothing, nor does completing after cancelling. - bool _isCanceled = false; + /// The operation controlled by this completer. + late final operation = CancelableOperation._(this); /// Creates a new completer for a [CancelableOperation]. /// @@ -234,6 +264,16 @@ class CancelableCompleter { /// The [onCancel] function will be called at most once. CancelableCompleter({FutureOr Function()? onCancel}) : _onCancel = onCancel; + /// Whether the [_inner] completer has been completed. + /// + /// At this point it's no longer possible to cancel the operation. + bool get _isCompleted => _cancelCompleter == null; + + /// Whether the completer was canceled before the result was ready. + /// + /// At this point, it's no longer possible to complete the operation. + bool get _isCanceled => _inner == null; + /// Whether the [complete] or [completeError] have been called. /// /// Once this completer has been completed with either a result or error, @@ -242,7 +282,7 @@ class CancelableCompleter { /// If [complete] was called with a [Future] argument, this completer may be /// completed before it's [operation] is completed. In that case the /// [operation] may still be canceled before the result is available. - bool get isCompleted => _isCompleted; + bool get isCompleted => !_mayComplete; /// Whether the completer was canceled before the result was ready. bool get isCanceled => _isCanceled; @@ -260,41 +300,47 @@ class CancelableCompleter { /// has been called once. /// The [isCompleted] is true when either of these methods have been called. void complete([FutureOr? value]) { - if (_isCompleted) throw StateError('Operation already completed'); - _isCompleted = true; + if (!_mayComplete) throw StateError('Operation already completed'); + _mayComplete = false; if (value is! Future) { - if (_isCanceled) return; - _inner.complete(value); + _completeNow()?.complete(value); return; } - if (_isCanceled) { + if (_inner == null) { // Make sure errors from [value] aren't top-leveled. value.ignore(); return; } value.then((result) { - if (_isCanceled) return; - _inner.complete(result); + _completeNow()?.complete(result); }, onError: (Object error, StackTrace stackTrace) { - if (_isCanceled) return; - _inner.completeError(error, stackTrace); + _completeNow()?.completeError(error, stackTrace); }); } + /// Completer to use for completing with a result. + /// + /// Returns `null` if it's not possible to complete any more. + /// Sets [_cancelCompleter] to `null` if returning non-`null`. + Completer? _completeNow() { + var inner = _inner; + if (inner == null) return null; + _cancelCompleter = null; + return inner; + } + /// Completes [operation] with [error] and [stackTrace]. /// /// This method may not be called after either [complete] or [completeError] /// has been called once. /// The [isCompleted] is true when either of these methods have been called. void completeError(Object error, [StackTrace? stackTrace]) { - if (_isCompleted) throw StateError('Operation already completed'); - _isCompleted = true; - - if (_isCanceled) return; - _inner.completeError(error, stackTrace); + if (!_mayComplete) throw StateError('Operation already completed'); + _mayComplete = false; + _completeNow()?.completeError(error, stackTrace); } /// Cancels the operation. @@ -310,14 +356,14 @@ class CancelableCompleter { /// The completer can be cancelled until the result becomes available, /// even if [isCompleted] is true. Future _cancel() { - if (_inner.isCompleted) return Future.value(null); + var cancelCompleter = _cancelCompleter; + if (cancelCompleter == null) return Future.value(null); - if (!_isCanceled) { - _isCanceled = true; + if (_inner != null) { + _inner = null; var onCancel = _onCancel; - _cancelCompleter - .complete(onCancel == null ? null : Future.sync(onCancel)); + cancelCompleter.complete(onCancel == null ? null : Future.sync(onCancel)); } - return _cancelCompleter.future; + return cancelCompleter.future; } } From 0d01690b76162dbf7e0ecd41abfc8a600dd9e728 Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Fri, 18 Mar 2022 11:12:33 +0100 Subject: [PATCH 212/260] Revert change of visible behavior in `CancelableOperation`. (dart-lang/async#208) * Revert change of visible behavior in `CancelableOperation`. A prior change made it so that a `.then` on a `CancelableOperation` would call the `onValue`/`onError` callback even if the returned operation was cancelled beteen the time of the `then` and the time the original operation completed. The result would not show up in the cancelled operation, but the callbacks would run. This change blocks calling the callbacks if the returned operation has been cancelled. --- pkgs/async/lib/src/cancelable_operation.dart | 52 +++++++++++++++---- .../async/test/cancelable_operation_test.dart | 39 ++++++++++++++ 2 files changed, 82 insertions(+), 9 deletions(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 394ba07e..9e3a7354 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -153,16 +153,50 @@ class CancelableOperation { final completer = CancelableCompleter(onCancel: propagateCancel ? cancel : null); - _completer._inner?.future - .then(onValue, onError: onError) - .then(completer.complete, onError: completer.completeError); - _completer._cancelCompleter?.future.then((_) { - if (onCancel != null) { - completer.complete(Future.sync(onCancel)); - } else { - completer._cancel(); + // if `_completer._inner` completes before `completer` is cancelled + // call `onValue` or `onError` with the result, and complete `completer` + // with the result of that call (unless cancelled in the meantime). + // + // If `_completer._cancelCompleter` completes (always with a value) + // before `completer` is cancelled, then call `onCancel` (if supplied) + // with that that value and complete `completer` with the result of that + // call (unless cancelled in the meantime). + // + // If any of the callbacks throw synchronously, the `completer` is + // completed with that error. + // + // If no `onCancel` is provided, and `_completer._cancelCompleter` + // completes before `completer` is cancelled, + // then cancel `cancelCompleter`. (Cancelling twice is safe.) + + _completer._inner?.future.then((value) { + if (completer.isCanceled) return; + try { + completer.complete(onValue(value)); + } catch (error, stack) { + completer.completeError(error, stack); } - }); + }, + onError: onError == null + ? completer.completeError // Is ignored if already cancelled. + : (Object error, StackTrace stack) { + if (completer.isCanceled) return; + try { + completer.complete(onError(error, stack)); + } catch (error2, stack2) { + completer.completeError(error2, stack2); + } + }); + _completer._cancelCompleter?.future.whenComplete(onCancel == null + ? completer._cancel + : () { + if (completer.isCanceled) return; + try { + completer.complete(onCancel()); + } catch (error, stack) { + completer.completeError(error, stack); + } + }); return completer.operation; } diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index cf2fd8b9..5e56b614 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -495,6 +495,45 @@ void main() { expect(originalCompleter.isCanceled, false); }); + + test('onValue callback not called after cancel', () async { + var called = false; + onValue = expectAsync1((_) { + called = true; + fail("onValue unreachable"); + }, count: 0); + + await runThen().cancel(); + originalCompleter.complete(0); + await flushMicrotasks(); + expect(called, false); + }); + + test('onError callback not called after cancel', () async { + var called = false; + onError = expectAsync2((_, __) { + called = true; + fail("onError unreachable"); + }, count: 0); + + await runThen().cancel(); + originalCompleter.completeError("Error", StackTrace.empty); + await flushMicrotasks(); + expect(called, false); + }); + + test('onCancel callback not called after cancel', () async { + var called = false; + onCancel = expectAsync0(() { + called = true; + fail("onCancel unreachable"); + }, count: 0); + + await runThen().cancel(); + await originalCompleter.operation.cancel(); + await flushMicrotasks(); + expect(called, false); + }); }); }); From 45ae0cf5c6abe0340f820c42bb0ff1f35a29e86a Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Mon, 21 Mar 2022 16:09:09 +0100 Subject: [PATCH 213/260] Make ephemeral async cache clear sooner. (dart-lang/async#207) The `AsyncCache.ephemeral()` cache would invalidate the `fetch` cache after a `Duration.zero` wait. That allows a large number of microtasks to happen between the `fetch` completing and the cache being invalidated. Instead the cache is now invalidated immediately when the fetched request completes. --- pkgs/async/CHANGELOG.md | 3 ++ pkgs/async/lib/src/async_cache.dart | 31 +++++++++----- pkgs/async/test/async_cache_test.dart | 58 +++++++++++++++++---------- 3 files changed, 61 insertions(+), 31 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index aabfb367..0086e7be 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -9,6 +9,9 @@ * Update `StreamGroup` methods that return a `Future` today to return a `Future` instead. +* Make `AsyncCache.ephemeral` invalidate itself immediately when the returned + future completes, rather than wait for a later timer event. + ## 2.8.2 * Deprecate `EventSinkBase`, `StreamSinkBase`, `IOSinkBase`. diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index 99b3881f..be7434f8 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -26,7 +26,10 @@ import 'package:async/async.dart'; /// [fake_async]: https://pub.dev/packages/fake_async class AsyncCache { /// How long cached values stay fresh. - final Duration _duration; + /// + /// Set to `null` for ephemeral caches, which only stay alive until the + /// future completes. + final Duration? _duration; /// Cached results of a previous [fetchStream] call. StreamSplitter? _cachedStreamSplitter; @@ -42,14 +45,14 @@ class AsyncCache { /// The [duration] starts counting after the Future returned by [fetch] /// completes, or after the Stream returned by [fetchStream] emits a done /// event. - AsyncCache(this._duration); + AsyncCache(Duration duration) : _duration = duration; /// Creates a cache that invalidates after an in-flight request is complete. /// /// An ephemeral cache guarantees that a callback function will only be /// executed at most once concurrently. This is useful for requests for which /// data is updated frequently but stale data is acceptable. - factory AsyncCache.ephemeral() => AsyncCache(Duration.zero); + AsyncCache.ephemeral() : _duration = null; /// Returns a cached value from a previous call to [fetch], or runs [callback] /// to compute a new one. @@ -60,12 +63,8 @@ class AsyncCache { if (_cachedStreamSplitter != null) { throw StateError('Previously used to cache via `fetchStream`'); } - final result = _cachedValueFuture ??= callback(); - try { - return await result; - } finally { - _startStaleTimer(); - } + return _cachedValueFuture ??= callback() + ..whenComplete(_startStaleTimer).ignore(); } /// Returns a cached stream from a previous call to [fetchStream], or runs @@ -74,6 +73,13 @@ class AsyncCache { /// If [fetchStream] has been called recently enough, returns a copy of its /// previous return value. Otherwise, runs [callback] and returns its new /// return value. + /// + /// Each call to this function returns a stream which replays the same events, + /// which means that all stream events are cached until this cache is + /// invalidated. + /// + /// Only starts counting time after the stream has been listened to, + /// and it has completed with a `done` event. Stream fetchStream(Stream Function() callback) { if (_cachedValueFuture != null) { throw StateError('Previously used to cache via `fetch`'); @@ -98,6 +104,11 @@ class AsyncCache { } void _startStaleTimer() { - _stale = Timer(_duration, invalidate); + var duration = _duration; + if (duration != null) { + _stale = Timer(duration, invalidate); + } else { + invalidate(); + } } } diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 3ceda792..e8ff132c 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -26,15 +26,43 @@ void main() { 'Expensive'); }); - test('should not fetch via callback when a future is in-flight', () async { - // No actual caching is done, just avoid duplicate requests. - cache = AsyncCache.ephemeral(); - - var completer = Completer(); - expect(cache.fetch(() => completer.future), completion('Expensive')); - expect(cache.fetch(expectAsync0(() async => 'fake', count: 0)), - completion('Expensive')); - completer.complete('Expensive'); + group('ephemeral cache', () { + test('should not fetch via callback when a future is in-flight', () async { + // No actual caching is done, just avoid duplicate requests. + cache = AsyncCache.ephemeral(); + + var completer = Completer(); + expect(cache.fetch(() => completer.future), completion('Expensive')); + expect(cache.fetch(expectAsync0(() async => 'fake', count: 0)), + completion('Expensive')); + completer.complete('Expensive'); + }); + + test('should fetch via callback when the in-flight future completes', + () async { + // No actual caching is done, just avoid duplicate requests. + cache = AsyncCache.ephemeral(); + + var fetched = cache.fetch(() async => "first"); + expect(fetched, completion('first')); + expect( + cache.fetch(expectAsync0(() async => fail('not called'), count: 0)), + completion('first')); + await fetched; + expect(cache.fetch(() async => 'second'), completion('second')); + }); + + test('should invalidate even if the future throws an exception', () async { + cache = AsyncCache.ephemeral(); + + Future throwingCall() async => throw Exception(); + await expectLater(cache.fetch(throwingCall), throwsA(isException)); + // To let the timer invalidate the cache + await Future.delayed(Duration(milliseconds: 5)); + + Future call() async => 'Completed'; + expect(await cache.fetch(call), 'Completed', reason: 'Cache invalidates'); + }); }); test('should fetch via a callback again when cache expires', () { @@ -158,16 +186,4 @@ void main() { })); expect(cache.fetchStream(call).toList(), completion(['1', '2', '3'])); }); - - test('should invalidate even if the future throws an exception', () async { - cache = AsyncCache.ephemeral(); - - Future throwingCall() async => throw Exception(); - await expectLater(cache.fetch(throwingCall), throwsA(isException)); - // To let the timer invalidate the cache - await Future.delayed(Duration(milliseconds: 5)); - - Future call() async => 'Completed'; - expect(await cache.fetch(call), 'Completed', reason: 'Cache invalidates'); - }); } From c45ad721d94db2c326456c823e4ef8ab5f1b608e Mon Sep 17 00:00:00 2001 From: "Lasse R.H. Nielsen" Date: Wed, 23 Mar 2022 18:02:26 +0100 Subject: [PATCH 214/260] Deprecate `AsyncCache.fetchStream`. (dart-lang/async#209) * Deprecate `AsyncCache.fetchStream`. The feature complicates the `AsyncCache` class, and would be a better fit for a separate class. The functionality is not great. Since it returns the "same" stream each time the cache is queried (until evaluated), it actually caches all stream events The invalidation is tricky because the timer doesn't start until you *listen* to the stream. In practice, people will usually listen to the stream they fetch. It also seems that no-one ever used the functionality. There is no use inside Google, or in [Github](https://github.com/search?q=%22.fetchStream%22+language%3ADart+-filename%3Aasync_cache.dart+-filename%3Aasync_cache_test.dart&type=code), outside of the package's own tests. It's a better fit for a separate class, but it also appears to not be used anywhere --- pkgs/async/CHANGELOG.md | 2 ++ pkgs/async/lib/src/async_cache.dart | 5 +++-- pkgs/async/test/async_cache_test.dart | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 0086e7be..1315bdcc 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -9,6 +9,8 @@ * Update `StreamGroup` methods that return a `Future` today to return a `Future` instead. +* Deprecated `AsyncCache.fetchStream`. + * Make `AsyncCache.ephemeral` invalidate itself immediately when the returned future completes, rather than wait for a later timer event. diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index be7434f8..b0551823 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -31,7 +31,7 @@ class AsyncCache { /// future completes. final Duration? _duration; - /// Cached results of a previous [fetchStream] call. + /// Cached results of a previous `fetchStream` call. StreamSplitter? _cachedStreamSplitter; /// Cached results of a previous [fetch] call. @@ -43,7 +43,7 @@ class AsyncCache { /// Creates a cache that invalidates its contents after [duration] has passed. /// /// The [duration] starts counting after the Future returned by [fetch] - /// completes, or after the Stream returned by [fetchStream] emits a done + /// completes, or after the Stream returned by `fetchStream` emits a done /// event. AsyncCache(Duration duration) : _duration = duration; @@ -80,6 +80,7 @@ class AsyncCache { /// /// Only starts counting time after the stream has been listened to, /// and it has completed with a `done` event. + @Deprecated("Feature will be removed") Stream fetchStream(Stream Function() callback) { if (_cachedValueFuture != null) { throw StateError('Previously used to cache via `fetch`'); diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index e8ff132c..4948e530 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -2,6 +2,8 @@ // 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. +// ignore_for_file: deprecated_member_use_from_same_package + import 'dart:async'; import 'package:async/async.dart'; From 510d42b90958f06799fca8aaba4bbf09c6b019ff Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 28 Mar 2022 12:28:17 -0700 Subject: [PATCH 215/260] Change the default propagateCancel argument (dart-lang/async#213) Closes dart-lang/async#212 Authors are more likely to expect propagation, and not cancelling is often a hidden inefficiency which may go unnoticed and untested. Update the doc comment to focus on the 3 ways an operation can end - as a value, an error, or a cancellation. This will hopefully make it more clear that the `onCancel` callback does not relate to the cancellation of the returned operation. --- pkgs/async/CHANGELOG.md | 12 +++--- pkgs/async/lib/src/cancelable_operation.dart | 41 ++++++++++++-------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1315bdcc..23263444 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,16 +1,18 @@ ## 2.9.0-dev +* **Potentially Breaking** The default `propagateCancel` argument to + `CancelableOperation.then` changed from `false` to `true`. In most usages this + won't have a meaningful difference in behavior, but in usages where the + behavior is important propagation is the more common need. If there are any + `CancelableOperation` with multiple listeners where canceling subsequent + computation using `.then` shouldn't also cancel the original operation, pass + `propagateCancel: false`. * Add `StreamExtensions.firstOrNull`. - * Add a `CancelableOperation.fromSubscription()` static factory. - * Add a `CancelableOperation.race()` static method. - * Update `StreamGroup` methods that return a `Future` today to return a `Future` instead. - * Deprecated `AsyncCache.fetchStream`. - * Make `AsyncCache.ephemeral` invalidate itself immediately when the returned future completes, rather than wait for a later timer event. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 9e3a7354..6ee350e4 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -130,26 +130,33 @@ class CancelableOperation { return completer.future; } - /// Creates a new cancelable operation to be completed - /// when this operation completes or is cancelled. - /// - /// The [onValue] and [onError] callbacks behave in the same way as - /// for [Future.then], and the result of those callbacks is used to complete - /// the returned cancelable operation. - /// - /// If [onCancel] is provided, and the this operation is canceled, - /// the [onCancel] callback is called and the returned operation completes - /// with the result returned by that call. - /// - /// If [onCancel] is not provided, and this operation is canceled, then the - /// returned operation is also canceled. - /// - /// If [propagateCancel] is `true` and the returned operation is canceled - /// then this operation is canceled. The default is `false`. + /// Creates a new cancelable operation to be completed when this operation + /// completes normally or as an error, or is cancelled. + /// + /// If this operation completes normally the [value] is passed to [onValue] + /// and the returned operation is completed with the result. + /// + /// If this operation completes as an error, and no [onError] callback is + /// provided, the returned operation is completed with the same error and + /// stack trace. + /// If this operation completes as an error, and an [onError] callback is + /// provided, the returned operation is completed with the result. + /// + /// If this operation is canceled, and no [onCancel] callback is provided, + /// the returned operation is canceled. + /// If this operation is canceled, and an [onCancel] callback is provided, + /// the returned operation is completed with the result. + /// + /// If the returned operation is canceled before this operation completes or + /// is canceled, the [onValue], [onError], and [onCancel] callbacks will not + /// be invoked. If [propagateCancel] is `true` (the default) then this + /// operation is canceled as well. Pass `false` if there are multiple + /// listeners on this operation and canceling the [onValue], [onError], and + /// [onCancel] callbacks should not cancel the other listeners. CancelableOperation then(FutureOr Function(T) onValue, {FutureOr Function(Object, StackTrace)? onError, FutureOr Function()? onCancel, - bool propagateCancel = false}) { + bool propagateCancel = true}) { final completer = CancelableCompleter(onCancel: propagateCancel ? cancel : null); From a558bb3023deb853d6a5a3d140da41bd89affed4 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 31 Mar 2022 14:34:23 -0700 Subject: [PATCH 216/260] Prepare to publish (dart-lang/async#214) Drop `-dev` postfix. --- pkgs/async/CHANGELOG.md | 2 +- pkgs/async/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 23263444..b5b5c9ec 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,4 @@ -## 2.9.0-dev +## 2.9.0 * **Potentially Breaking** The default `propagateCancel` argument to `CancelableOperation.then` changed from `false` to `true`. In most usages this diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 10d7d8fb..2275bce5 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.9.0-dev +version: 2.9.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async From c3fe316ef82c263baa8c579f0fd571b6e5077e7e Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 18 Apr 2022 14:52:35 -0700 Subject: [PATCH 217/260] Add CancelableOperation.thenOperation (dart-lang/async#211) Towards dart-lang/async#210 Once combined with `Completer.completeOperation` this signature adds significant flexibility for chaining Cancelable work following other async work. Move the existing `.then` implementation to `.thenOperation` since the latter is more general. Co-authored-by: Lasse R.H. Nielsen --- pkgs/async/CHANGELOG.md | 5 + pkgs/async/lib/src/cancelable_operation.dart | 78 +++++- pkgs/async/pubspec.yaml | 2 +- .../async/test/cancelable_operation_test.dart | 228 ++++++++++++++++++ 4 files changed, 301 insertions(+), 12 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index b5b5c9ec..38f8465c 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.10.0-dev + +* Add `CancelableOperation.thenOperation` which gives more flexibility to + complete the resulting operation. + ## 2.9.0 * **Potentially Breaking** The default `propagateCancel` argument to diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 6ee350e4..ee9ff9da 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -133,7 +133,7 @@ class CancelableOperation { /// Creates a new cancelable operation to be completed when this operation /// completes normally or as an error, or is cancelled. /// - /// If this operation completes normally the [value] is passed to [onValue] + /// If this operation completes normally the value is passed to [onValue] /// and the returned operation is completed with the result. /// /// If this operation completes as an error, and no [onError] callback is @@ -147,6 +147,11 @@ class CancelableOperation { /// If this operation is canceled, and an [onCancel] callback is provided, /// the returned operation is completed with the result. /// + /// At most one of [onValue], [onError], or [onCancel] will be called. + /// If any of [onValue], [onError], or [onCancel] throw a synchronous error, + /// or return a `Future` that completes as an error, the error will be + /// forwarded through the returned operation. + /// /// If the returned operation is canceled before this operation completes or /// is canceled, the [onValue], [onError], and [onCancel] callbacks will not /// be invoked. If [propagateCancel] is `true` (the default) then this @@ -154,8 +159,59 @@ class CancelableOperation { /// listeners on this operation and canceling the [onValue], [onError], and /// [onCancel] callbacks should not cancel the other listeners. CancelableOperation then(FutureOr Function(T) onValue, - {FutureOr Function(Object, StackTrace)? onError, - FutureOr Function()? onCancel, + {FutureOr Function(Object, StackTrace)? onError, + FutureOr Function()? onCancel, + bool propagateCancel = true}) => + thenOperation((value, completer) { + completer.complete(onValue(value)); + }, + onError: onError == null + ? null + : (error, stackTrace, completer) { + completer.complete(onError(error, stackTrace)); + }, + onCancel: onCancel == null + ? null + : (completer) { + completer.complete(onCancel()); + }, + propagateCancel: propagateCancel); + + /// Creates a new cancelable operation to be completed when this operation + /// completes normally or as an error, or is cancelled. + /// + /// If this operation completes normally the value is passed to [onValue] + /// with a [CancelableCompleter] controlling the returned operation. + /// + /// If this operation completes as an error, and no [onError] callback is + /// provided, the returned operation is completed with the same error and + /// stack trace. + /// If this operation completes as an error, and an [onError] callback is + /// provided, the error and stack trace are passed to [onError] with a + /// [CancelableCompleter] controlling the returned operation. + /// + /// If this operation is canceled, and no [onCancel] callback is provided, + /// the returned operation is canceled. + /// If this operation is canceled, and an [onCancel] callback is provided, + /// the [onCancel] callback is called with a [CancelableCompleter] controlling + /// the returned operation. + /// + /// At most one of [onValue], [onError], or [onCancel] will be called. + /// If any of [onValue], [onError], or [onCancel] throw a synchronous error, + /// or return a `Future` that completes as an error, the error will be + /// forwarded through the returned operation. + /// + /// If the returned operation is canceled before this operation completes or + /// is canceled, the [onValue], [onError], and [onCancel] callbacks will not + /// be invoked. If [propagateCancel] is `true` (the default) then this + /// operation is canceled as well. Pass `false` if there are multiple + /// listeners on this operation and canceling the [onValue], [onError], and + /// [onCancel] callbacks should not cancel the other listeners. + CancelableOperation thenOperation( + FutureOr Function(T, CancelableCompleter) onValue, + {FutureOr Function(Object, StackTrace, CancelableCompleter)? + onError, + FutureOr Function(CancelableCompleter)? onCancel, bool propagateCancel = true}) { final completer = CancelableCompleter(onCancel: propagateCancel ? cancel : null); @@ -176,35 +232,35 @@ class CancelableOperation { // completes before `completer` is cancelled, // then cancel `cancelCompleter`. (Cancelling twice is safe.) - _completer._inner?.future.then((value) { + _completer._inner?.future.then((value) async { if (completer.isCanceled) return; try { - completer.complete(onValue(value)); + await onValue(value, completer); } catch (error, stack) { completer.completeError(error, stack); } }, onError: onError == null ? completer.completeError // Is ignored if already cancelled. - : (Object error, StackTrace stack) { + : (Object error, StackTrace stack) async { if (completer.isCanceled) return; try { - completer.complete(onError(error, stack)); + await onError(error, stack, completer); } catch (error2, stack2) { - completer.completeError(error2, stack2); + completer.completeError( + error2, identical(error, error2) ? stack : stack2); } }); _completer._cancelCompleter?.future.whenComplete(onCancel == null ? completer._cancel - : () { + : () async { if (completer.isCanceled) return; try { - completer.complete(onCancel()); + await onCancel(completer); } catch (error, stack) { completer.completeError(error, stack); } }); - return completer.operation; } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 2275bce5..cb6f348e 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.9.0 +version: 2.10.0-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 5e56b614..0914ddc7 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -537,6 +537,234 @@ void main() { }); }); + group('thenOperation', () { + late void Function(int, CancelableCompleter) onValue; + void Function(Object, StackTrace, CancelableCompleter)? onError; + void Function(CancelableCompleter)? onCancel; + late bool propagateCancel; + late CancelableCompleter originalCompleter; + + setUp(() { + // Initialize all functions to ones that expect to not be called. + onValue = expectAsync2((value, completer) => completer.complete('$value'), + count: 0, id: 'onValue'); + onError = null; + onCancel = null; + propagateCancel = false; + originalCompleter = CancelableCompleter(); + }); + + CancelableOperation runThenOperation() { + return originalCompleter.operation.thenOperation(onValue, + onError: onError, + onCancel: onCancel, + propagateCancel: propagateCancel); + } + + group('original operation completes successfully', () { + test('onValue completes successfully', () { + onValue = + expectAsync2((v, c) => c.complete('$v'), count: 1, id: 'onValue'); + + expect(runThenOperation().value, completion('1')); + originalCompleter.complete(1); + }); + + test('onValue throws error', () { + // expectAsync1 only works with functions that do not throw. + onValue = (_, __) => throw 'error'; + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.complete(1); + }); + + test('onValue completes operation as error', () { + onValue = expectAsync2( + (_, completer) => completer.completeError('error'), + count: 1, + id: 'onValue'); + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.complete(1); + }); + + test('onValue returns a Future that throws error', () { + onValue = expectAsync2((_, completer) => Future.error('error'), + count: 1, id: 'onValue'); + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.complete(1); + }); + + test('and returned operation is canceled', () async { + onValue = expectAsync2((_, __) => throw 'never called', count: 0); + runThenOperation().cancel(); + // onValue should not be called. + originalCompleter.complete(1); + }); + }); + + group('original operation completes with error', () { + test('onError not set', () { + onError = null; + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.completeError('error'); + }); + + test('onError completes operation', () { + onError = expectAsync3((e, s, c) => c.complete('onError caught $e'), + count: 1, id: 'onError'); + + expect(runThenOperation().value, completion('onError caught error')); + originalCompleter.completeError('error'); + }); + + test('onError throws', () { + // expectAsync3 does not work with functions that throw. + onError = (e, s, c) => throw 'onError caught $e'; + + expect(runThenOperation().value, throwsA('onError caught error')); + originalCompleter.completeError('error'); + }); + + test('onError returns Future that throws error', () { + onError = expectAsync3((e, s, c) => Future.error('onError caught $e'), + count: 1, id: 'onError'); + + expect(runThenOperation().value, throwsA('onError caught error')); + originalCompleter.completeError('error'); + }); + + test('onError completes operation as an error', () { + onError = expectAsync3( + (e, s, c) => c.completeError('onError caught $e'), + count: 1, + id: 'onError'); + + expect(runThenOperation().value, throwsA('onError caught error')); + originalCompleter.completeError('error'); + }); + + test('and returned operation is canceled with propagateCancel = false', + () async { + onError = expectAsync3((e, s, c) {}, count: 0); + + runThenOperation().cancel(); + + // onError should not be called. + originalCompleter.completeError('error'); + }); + }); + + group('original operation canceled', () { + test('onCancel not set', () async { + onCancel = null; + + final operation = runThenOperation(); + + await expectLater(originalCompleter.operation.cancel(), completes); + expect(operation.isCanceled, true); + }); + + test('onCancel completes successfully', () { + onCancel = expectAsync1((c) => c.complete('canceled'), + count: 1, id: 'onCancel'); + + expect(runThenOperation().value, completion('canceled')); + originalCompleter.operation.cancel(); + }); + + test('onCancel throws error', () { + // expectAsync0 only works with functions that do not throw. + onCancel = (_) => throw 'error'; + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.operation.cancel(); + }); + + test('onCancel completes operation as error', () { + onCancel = expectAsync1((c) => c.completeError('error'), + count: 1, id: 'onCancel'); + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.operation.cancel(); + }); + + test('onCancel returns Future that throws error', () { + onCancel = expectAsync1((c) => Future.error('error'), + count: 1, id: 'onCancel'); + + expect(runThenOperation().value, throwsA('error')); + originalCompleter.operation.cancel(); + }); + + test('after completing with a future does not invoke `onValue`', + () async { + onValue = expectAsync2((_, __) {}, count: 0); + onCancel = null; + var operation = runThenOperation(); + var workCompleter = Completer(); + originalCompleter.complete(workCompleter.future); + var cancelation = originalCompleter.operation.cancel(); + expect(originalCompleter.isCanceled, true); + workCompleter.complete(0); + await cancelation; + expect(operation.isCanceled, true); + await workCompleter.future; + }); + + test('after the value is completed invokes `onValue`', () { + onValue = expectAsync2((v, c) => c.complete('foo'), count: 1); + onCancel = expectAsync1((_) {}, count: 0); + originalCompleter.complete(0); + originalCompleter.operation.cancel(); + var operation = runThenOperation(); + expect(operation.value, completion('foo')); + expect(operation.isCanceled, false); + }); + }); + + group('returned operation canceled', () { + test('propagateCancel is true', () async { + propagateCancel = true; + + await runThenOperation().cancel(); + + expect(originalCompleter.isCanceled, true); + }); + + test('propagateCancel is false', () async { + propagateCancel = false; + + await runThenOperation().cancel(); + + expect(originalCompleter.isCanceled, false); + }); + + test('onValue callback not called after cancel', () async { + onValue = expectAsync2((_, c) {}, count: 0); + + await runThenOperation().cancel(); + originalCompleter.complete(0); + }); + + test('onError callback not called after cancel', () async { + onError = expectAsync3((_, __, ___) {}, count: 0); + + await runThenOperation().cancel(); + originalCompleter.completeError("Error", StackTrace.empty); + }); + + test('onCancel callback not called after cancel', () async { + onCancel = expectAsync1((_) {}, count: 0); + + await runThenOperation().cancel(); + await originalCompleter.operation.cancel(); + }); + }); + }); + group('race()', () { late bool canceled1; late CancelableCompleter completer1; From c923caa866383b210457543c4bc5514954e4fb94 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 19 Apr 2022 11:20:09 -0700 Subject: [PATCH 218/260] Add CancelableCompleter.completeOperation (dart-lang/async#215) Towards dart-lang/async#210 Combined with `CancelableOperation.thenOperation` this allows chaining cancelable work that can be canceled at multiple points. --- pkgs/async/CHANGELOG.md | 1 + pkgs/async/lib/src/cancelable_operation.dart | 28 ++++++ .../async/test/cancelable_operation_test.dart | 91 +++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 38f8465c..6e2add92 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -2,6 +2,7 @@ * Add `CancelableOperation.thenOperation` which gives more flexibility to complete the resulting operation. +* Add `CancelableCompleter.completeOperation`. ## 2.9.0 diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index ee9ff9da..65ecabc8 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -418,6 +418,34 @@ class CancelableCompleter { }); } + /// Makes this [CancelableCompleter.operation] complete with the same result + /// as [result]. + /// + /// If [propagateCancel] is `true` (the default), and the [operation] of this + /// completer is canceled before [result] completes, then [result] is also + /// canceled. + void completeOperation(CancelableOperation result, + {bool propagateCancel = true}) { + if (!_mayComplete) throw StateError("Already completed"); + _mayComplete = false; + if (isCanceled) { + if (propagateCancel) result.cancel(); + result.value.ignore(); + return; + } + result.then((value) { + _inner?.complete( + value); // _inner is set to null if this.operation is cancelled. + }, onError: (error, stack) { + _inner?.completeError(error, stack); + }, onCancel: () { + operation.cancel(); + }); + if (propagateCancel) { + _cancelCompleter?.future.whenComplete(result.cancel); + } + } + /// Completer to use for completing with a result. /// /// Returns `null` if it's not possible to complete any more. diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 0914ddc7..f073e80f 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -54,6 +54,39 @@ void main() { expect(completer.operation.isCompleted, isTrue); }); + test('sends values from a cancelable operation to the future', () { + expect(completer.operation.value, completion(equals(1))); + completer + .completeOperation(CancelableOperation.fromFuture(Future.value(1))); + }); + + test('sends values from a completed cancelable operation to the future', + () async { + final operation = CancelableOperation.fromFuture(Future.value(1)); + await operation.value; + expect(completer.operation.value, completion(equals(1))); + completer.completeOperation(operation); + }); + + test('sends errors from a cancelable operation to the future', () { + expect(completer.operation.value, throwsA('error')); + completer.completeOperation( + CancelableOperation.fromFuture(Future.error('error')..ignore())); + }); + + test('sends errors from a completed cancelable operation to the future', + () async { + final operation = + CancelableOperation.fromFuture(Future.error('error')..ignore()); + try { + await operation.value; + } on Object { + // ignore + } + expect(completer.operation.value, throwsA('error')); + completer.completeOperation(operation); + }); + test('sends values to valueOrCancellation', () { expect(completer.operation.valueOrCancellation(), completion(equals(1))); completer.complete(1); @@ -292,6 +325,64 @@ void main() { await flushMicrotasks(); expect(cancelCompleted, isTrue); }); + + group('completeOperation', () { + test('sends cancellation from a cancelable operation', () async { + final completer = CancelableCompleter(); + completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); + completer + .completeOperation(CancelableCompleter().operation..cancel()); + await completer.operation.valueOrCancellation(); + expect(completer.operation.isCanceled, true); + }); + + test('sends errors from a completed cancelable operation to the future', + () async { + final operation = CancelableCompleter().operation..cancel(); + await operation.valueOrCancellation(); + final completer = CancelableCompleter(); + completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); + completer.completeOperation(operation); + await completer.operation.valueOrCancellation(); + expect(completer.operation.isCanceled, true); + }); + + test('propagates cancellation', () { + final completer = CancelableCompleter(); + final operation = + CancelableCompleter(onCancel: expectAsync0(() {}, count: 1)) + .operation; + completer.completeOperation(operation); + completer.operation.cancel(); + }); + + test('propagates cancellation from already canceld completer', () async { + final completer = CancelableCompleter()..operation.cancel(); + await completer.operation.valueOrCancellation(); + final operation = + CancelableCompleter(onCancel: expectAsync0(() {}, count: 1)) + .operation; + completer.completeOperation(operation); + }); + test('cancel propagation can be disabled', () { + final completer = CancelableCompleter(); + final operation = + CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)) + .operation; + completer.completeOperation(operation, propagateCancel: false); + completer.operation.cancel(); + }); + + test('cancel propagation can be disabled from already canceled completed', + () async { + final completer = CancelableCompleter()..operation.cancel(); + await completer.operation.valueOrCancellation(); + final operation = + CancelableCompleter(onCancel: expectAsync0(() {}, count: 0)) + .operation; + completer.completeOperation(operation, propagateCancel: false); + }); + }); }); group('asStream()', () { From 874d5aea57a058ce2613981a56ef2ca82145d93a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 20 Oct 2022 12:07:37 -0700 Subject: [PATCH 219/260] Require Dart 2.18, latest lints, latest CI bits (dart-lang/async#223) --- pkgs/async/.github/dependabot.yml | 10 ++++++ pkgs/async/.github/workflows/test-package.yml | 10 +++--- pkgs/async/CHANGELOG.md | 3 +- pkgs/async/README.md | 4 +++ pkgs/async/analysis_options.yaml | 31 ++++++++++++++++--- pkgs/async/lib/async.dart | 7 +++-- pkgs/async/lib/src/async_cache.dart | 9 +++--- pkgs/async/lib/src/cancelable_operation.dart | 28 +++++++++-------- pkgs/async/lib/src/chunked_stream_reader.dart | 4 +-- pkgs/async/lib/src/delegate/event_sink.dart | 4 +-- pkgs/async/lib/src/delegate/sink.dart | 2 +- pkgs/async/lib/src/delegate/stream.dart | 2 +- .../lib/src/delegate/stream_consumer.dart | 2 +- pkgs/async/lib/src/delegate/stream_sink.dart | 4 +-- pkgs/async/lib/src/null_stream_sink.dart | 6 ++-- pkgs/async/lib/src/restartable_timer.dart | 4 +-- .../lib/src/result/capture_transformer.dart | 5 ++- pkgs/async/lib/src/result/error.dart | 2 +- .../lib/src/result/release_transformer.dart | 2 +- pkgs/async/lib/src/result/result.dart | 12 +++---- pkgs/async/lib/src/result/value.dart | 2 +- pkgs/async/lib/src/sink_base.dart | 28 ++++++++--------- pkgs/async/lib/src/stream_extensions.dart | 4 +-- pkgs/async/lib/src/stream_group.dart | 4 +-- pkgs/async/lib/src/stream_queue.dart | 24 +++++++------- pkgs/async/lib/src/stream_sink_completer.dart | 10 +++--- .../async/lib/src/stream_sink_extensions.dart | 2 +- .../lib/src/stream_sink_transformer.dart | 2 +- .../handler_transformer.dart | 6 ++-- .../reject_errors.dart | 2 +- .../stream_transformer_wrapper.dart | 2 +- pkgs/async/lib/src/stream_zip.dart | 2 +- pkgs/async/lib/src/subscription_stream.dart | 5 ++- pkgs/async/pubspec.yaml | 6 ++-- pkgs/async/test/async_cache_test.dart | 6 ++-- pkgs/async/test/byte_collection_test.dart | 2 +- .../async/test/cancelable_operation_test.dart | 18 +++++------ pkgs/async/test/chunked_stream_reader.dart | 2 +- pkgs/async/test/io_sink_impl.dart | 2 +- pkgs/async/test/lazy_stream_test.dart | 2 +- .../test/result/result_captureAll_test.dart | 2 +- .../test/result/result_flattenAll_test.dart | 2 +- pkgs/async/test/result/result_test.dart | 2 +- pkgs/async/test/sink_base_test.dart | 13 +++----- pkgs/async/test/stream_completer_test.dart | 2 +- pkgs/async/test/stream_group_test.dart | 4 +-- pkgs/async/test/stream_queue_test.dart | 4 +-- pkgs/async/test/subscription_stream_test.dart | 2 +- pkgs/async/test/utils.dart | 9 +++--- 49 files changed, 178 insertions(+), 144 deletions(-) create mode 100644 pkgs/async/.github/dependabot.yml diff --git a/pkgs/async/.github/dependabot.yml b/pkgs/async/.github/dependabot.yml new file mode 100644 index 00000000..71cdeea3 --- /dev/null +++ b/pkgs/async/.github/dependabot.yml @@ -0,0 +1,10 @@ +# Set update schedule for GitHub Actions +# See https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot + +version: 2 +updates: + +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "monthly" diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index fdddad8b..d257b9f9 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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 @@ -47,10 +47,10 @@ jobs: matrix: # Add macos-latest and/or windows-latest if relevant for this package. os: [ubuntu-latest] - sdk: [2.14.1, dev] + sdk: [2.18.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 diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 6e2add92..1b3144ce 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,8 +1,9 @@ -## 2.10.0-dev +## 2.10.0 * Add `CancelableOperation.thenOperation` which gives more flexibility to complete the resulting operation. * Add `CancelableCompleter.completeOperation`. +* Require Dart 2.18 ## 2.9.0 diff --git a/pkgs/async/README.md b/pkgs/async/README.md index f7ed8beb..b25b7407 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -1,3 +1,7 @@ +[![Dart CI](https://github.com/dart-lang/async/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/async/actions/workflows/test-package.yml) +[![pub package](https://img.shields.io/pub/v/async.svg)](https://pub.dev/packages/async) +[![package publisher](https://img.shields.io/pub/publisher/async.svg)](https://pub.dev/packages/async/publisher) + Contains utility classes in the style of `dart:async` to work with asynchronous computations. diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 5c781356..ca9be773 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,7 +1,30 @@ +# https://dart.dev/guides/language/analysis-options include: package:lints/recommended.yaml analyzer: - strong-mode: - implicit-casts: false - errors: - todo: ignore + language: + #strict-casts: true + #strict-inference: true + #strict-raw-types: true + +linter: + rules: + - always_declare_return_types + - avoid_dynamic_calls + - avoid_unused_constructor_parameters + - comment_references + - directives_ordering + - lines_longer_than_80_chars + - literal_only_boolean_expressions + - missing_whitespace_between_adjacent_strings + - no_adjacent_strings_in_list + - no_runtimeType_toString + - omit_local_variable_types + - package_api_docs + - prefer_relative_imports + - prefer_single_quotes + - test_types_in_equals + - throw_in_finally + - type_annotate_public_apis + - unnecessary_lambdas + - use_super_parameters diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 10e7fe79..1d7b797c 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -2,7 +2,8 @@ // 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. -/// Utilities that expand on the asynchronous features of the `dart:async` library. +/// Utilities that expand on the asynchronous features of the `dart:async` +/// library. /// /// {@youtube 560 315 https://www.youtube.com/watch?v=r0tHiCjW2w0} library async; @@ -11,6 +12,7 @@ export 'src/async_cache.dart'; export 'src/async_memoizer.dart'; export 'src/byte_collector.dart'; export 'src/cancelable_operation.dart'; +export 'src/chunked_stream_reader.dart'; export 'src/delegate/event_sink.dart'; export 'src/delegate/future.dart'; export 'src/delegate/sink.dart'; @@ -22,9 +24,9 @@ export 'src/future_group.dart'; export 'src/lazy_stream.dart'; export 'src/null_stream_sink.dart'; export 'src/restartable_timer.dart'; -export 'src/result/result.dart'; export 'src/result/error.dart'; export 'src/result/future.dart'; +export 'src/result/result.dart'; export 'src/result/value.dart'; export 'src/single_subscription_transformer.dart'; export 'src/sink_base.dart'; @@ -41,4 +43,3 @@ export 'src/stream_subscription_transformer.dart'; export 'src/stream_zip.dart'; export 'src/subscription_stream.dart'; export 'src/typed_stream_transformer.dart'; -export 'src/chunked_stream_reader.dart'; diff --git a/pkgs/async/lib/src/async_cache.dart b/pkgs/async/lib/src/async_cache.dart index b0551823..6fc7cb0e 100644 --- a/pkgs/async/lib/src/async_cache.dart +++ b/pkgs/async/lib/src/async_cache.dart @@ -4,7 +4,7 @@ import 'dart:async'; -import 'package:async/async.dart'; +import '../async.dart'; /// Runs asynchronous functions and caches the result for a period of time. /// @@ -21,9 +21,8 @@ import 'package:async/async.dart'; /// }); /// ``` /// -/// This class's timing can be mocked using [`fake_async`][fake_async]. -/// -/// [fake_async]: https://pub.dev/packages/fake_async +/// This class's timing can be mocked using +/// [`fake_async`](https://pub.dev/packages/fake_async). class AsyncCache { /// How long cached values stay fresh. /// @@ -80,7 +79,7 @@ class AsyncCache { /// /// Only starts counting time after the stream has been listened to, /// and it has completed with a `done` event. - @Deprecated("Feature will be removed") + @Deprecated('Feature will be removed') Stream fetchStream(Stream Function() callback) { if (_cachedValueFuture != null) { throw StateError('Previously used to cache via `fetch`'); diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 65ecabc8..443ee78a 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -17,7 +17,8 @@ class CancelableOperation { CancelableOperation._(this._completer); - /// Creates a [CancelableOperation] with the same result as the [result] future. + /// Creates a [CancelableOperation] with the same result as the [result] + /// future. /// /// When this operation is canceled, [onCancel] will be called and any value /// or error later produced by [result] will be discarded. @@ -34,10 +35,11 @@ class CancelableOperation { /// Creates a [CancelableOperation] wrapping [subscription]. /// - /// This overrides [subscription.onDone] and [subscription.onError] so that - /// the returned operation will complete when the subscription completes or - /// emits an error. When this operation is canceled or when it emits an error, - /// the subscription will be canceled (unlike + /// This overrides [StreamSubscription.onDone] and + /// [StreamSubscription.onError] so that the returned operation will complete + /// when the subscription completes or emits an error. + /// When this operation is canceled or when it emits an error, the + /// subscription will be canceled (unlike /// `CancelableOperation.fromFuture(subscription.asFuture())`). static CancelableOperation fromSubscription( StreamSubscription subscription) { @@ -62,24 +64,24 @@ class CancelableOperation { Iterable> operations) { operations = operations.toList(); if (operations.isEmpty) { - throw ArgumentError("May not be empty", "operations"); + throw ArgumentError('May not be empty', 'operations'); } var done = false; // Note: if one or more of the completers have already completed, // they're not actually cancelled by this. - Future _cancelAll() { + Future cancelAll() { done = true; return Future.wait(operations.map((operation) => operation.cancel())); } - var completer = CancelableCompleter(onCancel: _cancelAll); + var completer = CancelableCompleter(onCancel: cancelAll); for (var operation in operations) { operation.then((value) { - if (!done) _cancelAll().whenComplete(() => completer.complete(value)); + if (!done) cancelAll().whenComplete(() => completer.complete(value)); }, onError: (error, stackTrace) { if (!done) { - _cancelAll() + cancelAll() .whenComplete(() => completer.completeError(error, stackTrace)); } }); @@ -266,7 +268,7 @@ class CancelableOperation { /// Cancels this operation. /// - /// If this operation [isComplete] or [isCanceled] this call is ignored. + /// If this operation [isCompleted] or [isCanceled] this call is ignored. /// Returns the result of the `onCancel` callback, if one exists. Future cancel() => _completer._cancel(); @@ -388,7 +390,7 @@ class CancelableCompleter { /// /// If [value] is a [Future] the [operation] will complete /// with the result of that `Future` once it is available. - /// In that case [isComplete] will be `true` before the [operation] + /// In that case [isCompleted] will be `true` before the [operation] /// is complete. /// /// If the type [T] is not nullable [value] may be not be omitted or `null`. @@ -426,7 +428,7 @@ class CancelableCompleter { /// canceled. void completeOperation(CancelableOperation result, {bool propagateCancel = true}) { - if (!_mayComplete) throw StateError("Already completed"); + if (!_mayComplete) throw StateError('Already completed'); _mayComplete = false; if (isCanceled) { if (propagateCancel) result.cancel(); diff --git a/pkgs/async/lib/src/chunked_stream_reader.dart b/pkgs/async/lib/src/chunked_stream_reader.dart index fd9be921..1d92216a 100644 --- a/pkgs/async/lib/src/chunked_stream_reader.dart +++ b/pkgs/async/lib/src/chunked_stream_reader.dart @@ -63,8 +63,8 @@ class ChunkedStreamReader { /// Whether a read request is currently being processed. /// /// Is `true` while a request is in progress. - /// While a read request, like [readChunk] or [readStream], is being processed, - /// no new requests can be made. + /// While a read request, like [readChunk] or [readStream], is being + /// processed, no new requests can be made. /// New read attempts will throw instead. bool _reading = false; diff --git a/pkgs/async/lib/src/delegate/event_sink.dart b/pkgs/async/lib/src/delegate/event_sink.dart index c011af5f..34c119b5 100644 --- a/pkgs/async/lib/src/delegate/event_sink.dart +++ b/pkgs/async/lib/src/delegate/event_sink.dart @@ -18,7 +18,7 @@ class DelegatingEventSink implements EventSink { /// Creates a wrapper that coerces the type of [sink]. /// - /// Unlike [new DelegatingEventSink], this only requires its argument to be an + /// Unlike [DelegatingEventSink.new], this only requires its argument to be an /// instance of `EventSink`, not `EventSink`. This means that calls to /// [add] may throw a [TypeError] if the argument type doesn't match the /// reified type of [sink]. @@ -33,7 +33,7 @@ class DelegatingEventSink implements EventSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { _sink.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/delegate/sink.dart b/pkgs/async/lib/src/delegate/sink.dart index e0b03c57..a1954f0d 100644 --- a/pkgs/async/lib/src/delegate/sink.dart +++ b/pkgs/async/lib/src/delegate/sink.dart @@ -16,7 +16,7 @@ class DelegatingSink implements Sink { /// Creates a wrapper that coerces the type of [sink]. /// - /// Unlike [new DelegatingSink], this only requires its argument to be an + /// Unlike [DelegatingSink.new], this only requires its argument to be an /// instance of `Sink`, not `Sink`. This means that calls to [add] may /// throw a [TypeError] if the argument type doesn't match the reified type of /// [sink]. diff --git a/pkgs/async/lib/src/delegate/stream.dart b/pkgs/async/lib/src/delegate/stream.dart index 6c2031a4..68992d5b 100644 --- a/pkgs/async/lib/src/delegate/stream.dart +++ b/pkgs/async/lib/src/delegate/stream.dart @@ -12,7 +12,7 @@ import 'dart:async'; /// Note that this is identical to [StreamView] in `dart:async`. It's provided /// under this name for consistency with other `Delegating*` classes. class DelegatingStream extends StreamView { - DelegatingStream(Stream stream) : super(stream); + DelegatingStream(super.stream); /// Creates a wrapper which throws if [stream]'s events aren't instances of /// `T`. diff --git a/pkgs/async/lib/src/delegate/stream_consumer.dart b/pkgs/async/lib/src/delegate/stream_consumer.dart index 0dad5ffa..c911c414 100644 --- a/pkgs/async/lib/src/delegate/stream_consumer.dart +++ b/pkgs/async/lib/src/delegate/stream_consumer.dart @@ -18,7 +18,7 @@ class DelegatingStreamConsumer implements StreamConsumer { /// Creates a wrapper that coerces the type of [consumer]. /// - /// Unlike [new StreamConsumer], this only requires its argument to be an + /// Unlike [StreamConsumer.new], this only requires its argument to be an /// instance of `StreamConsumer`, not `StreamConsumer`. This means that /// calls to [addStream] may throw a [TypeError] if the argument type doesn't /// match the reified type of [consumer]. diff --git a/pkgs/async/lib/src/delegate/stream_sink.dart b/pkgs/async/lib/src/delegate/stream_sink.dart index f18d501f..e6edd2ff 100644 --- a/pkgs/async/lib/src/delegate/stream_sink.dart +++ b/pkgs/async/lib/src/delegate/stream_sink.dart @@ -21,7 +21,7 @@ class DelegatingStreamSink implements StreamSink { /// Creates a wrapper that coerces the type of [sink]. /// - /// Unlike [new StreamSink], this only requires its argument to be an instance + /// Unlike [StreamSink.new], this only requires its argument to be an instance /// of `StreamSink`, not `StreamSink`. This means that calls to [add] may /// throw a [TypeError] if the argument type doesn't match the reified type of /// [sink]. @@ -36,7 +36,7 @@ class DelegatingStreamSink implements StreamSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { _sink.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/null_stream_sink.dart b/pkgs/async/lib/src/null_stream_sink.dart index 34b100bb..4d9bad0d 100644 --- a/pkgs/async/lib/src/null_stream_sink.dart +++ b/pkgs/async/lib/src/null_stream_sink.dart @@ -8,7 +8,7 @@ import 'dart:async'; /// /// The sink silently drops events until [close] is called, at which point it /// throws [StateError]s when events are added. This is the same behavior as a -/// sink whose remote end has closed, such as when a [WebSocket] connection has +/// sink whose remote end has closed, such as when a `WebSocket` connection has /// been closed. /// /// This can be used when a sink is needed but no events are actually intended @@ -41,8 +41,8 @@ class NullStreamSink implements StreamSink { /// Creates a null sink. /// - /// If [done] is passed, it's used as the [Sink.done] future. Otherwise, a - /// completed future is used. + /// If [done] is passed, it's used as the [StreamSink.done] future. Otherwise, + /// a completed future is used. NullStreamSink({Future? done}) : done = done ?? Future.value(); /// Creates a null sink whose [done] future emits [error]. diff --git a/pkgs/async/lib/src/restartable_timer.dart b/pkgs/async/lib/src/restartable_timer.dart index 9ecf4431..1cff458f 100644 --- a/pkgs/async/lib/src/restartable_timer.dart +++ b/pkgs/async/lib/src/restartable_timer.dart @@ -23,8 +23,8 @@ class RestartableTimer implements Timer { /// Creates a new timer. /// - /// The [callback] function is invoked after the given [duration]. Unlike a - /// normal non-periodic [Timer], [callback] may be called more than once. + /// The [_callback] function is invoked after the given [_duration]. Unlike a + /// normal non-periodic [Timer], [_callback] may be called more than once. RestartableTimer(this._duration, this._callback) : _timer = Timer(_duration, _callback); diff --git a/pkgs/async/lib/src/result/capture_transformer.dart b/pkgs/async/lib/src/result/capture_transformer.dart index 1f58d066..39aaef9f 100644 --- a/pkgs/async/lib/src/result/capture_transformer.dart +++ b/pkgs/async/lib/src/result/capture_transformer.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'result.dart'; import 'capture_sink.dart'; +import 'result.dart'; /// A stream transformer that captures a stream of events into [Result]s. /// @@ -16,6 +16,5 @@ class CaptureStreamTransformer extends StreamTransformerBase> { @override Stream> bind(Stream source) => - Stream>.eventTransformed( - source, (sink) => CaptureSink(sink)); + Stream>.eventTransformed(source, CaptureSink.new); } diff --git a/pkgs/async/lib/src/result/error.dart b/pkgs/async/lib/src/result/error.dart index 9d0ba614..48f71b1d 100644 --- a/pkgs/async/lib/src/result/error.dart +++ b/pkgs/async/lib/src/result/error.dart @@ -50,7 +50,7 @@ class ErrorResult implements Result { if (errorHandler is ZoneBinaryCallback) { errorHandler(error, stackTrace); } else { - errorHandler(error); + (errorHandler as ZoneUnaryCallback)(error); } } diff --git a/pkgs/async/lib/src/result/release_transformer.dart b/pkgs/async/lib/src/result/release_transformer.dart index 2548ac9b..2f80d719 100644 --- a/pkgs/async/lib/src/result/release_transformer.dart +++ b/pkgs/async/lib/src/result/release_transformer.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'result.dart'; import 'release_sink.dart'; +import 'result.dart'; /// A transformer that releases result events as data and error events. class ReleaseStreamTransformer extends StreamTransformerBase, T> { diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 5f93b551..2de3902f 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -4,13 +4,13 @@ import 'dart:async'; +import '../stream_sink_transformer.dart'; import 'capture_sink.dart'; import 'capture_transformer.dart'; import 'error.dart'; import 'release_sink.dart'; import 'release_transformer.dart'; import 'value.dart'; -import '../stream_sink_transformer.dart'; /// The result of a computation. /// @@ -70,12 +70,12 @@ abstract class Result { /// Creates a `Result` holding a value. /// - /// Alias for [ValueResult.ValueResult]. + /// Alias for [ValueResult.new]. factory Result.value(T value) = ValueResult; /// Creates a `Result` holding an error. /// - /// Alias for [ErrorResult.ErrorResult]. + /// Alias for [ErrorResult.new]. factory Result.error(Object error, [StackTrace? stackTrace]) => ErrorResult(error, stackTrace); @@ -84,9 +84,7 @@ abstract class Result { /// The resulting future will never have an error. /// Errors have been converted to an [ErrorResult] value. static Future> capture(Future future) { - return future.then((value) => ValueResult(value), - onError: (Object error, StackTrace stackTrace) => - ErrorResult(error, stackTrace)); + return future.then(ValueResult.new, onError: ErrorResult.new); } /// Captures each future in [elements], @@ -139,7 +137,7 @@ abstract class Result { static Stream> captureStream(Stream source) => source.transform(CaptureStreamTransformer()); - /// Releases a stream of [result] values into a stream of the results. + /// Releases a stream of [source] values into a stream of the results. /// /// `Result` values of the source stream become value or error events in /// the returned stream as appropriate. diff --git a/pkgs/async/lib/src/result/value.dart b/pkgs/async/lib/src/result/value.dart index 7cfc4742..3872dd0b 100644 --- a/pkgs/async/lib/src/result/value.dart +++ b/pkgs/async/lib/src/result/value.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'result.dart'; import 'error.dart'; +import 'result.dart'; /// A result representing a returned value. class ValueResult implements Result { diff --git a/pkgs/async/lib/src/sink_base.dart b/pkgs/async/lib/src/sink_base.dart index 62ca9810..f1d7d14b 100644 --- a/pkgs/async/lib/src/sink_base.dart +++ b/pkgs/async/lib/src/sink_base.dart @@ -9,7 +9,7 @@ import 'package:meta/meta.dart'; import 'async_memoizer.dart'; -/// An abstract class that implements [EventSink] in terms of [onData], +/// An abstract class that implements [EventSink] in terms of [onAdd], /// [onError], and [onClose] methods. /// /// This takes care of ensuring that events can't be added after [close] is @@ -57,11 +57,11 @@ abstract class EventSinkBase implements EventSink { } } -/// An abstract class that implements [StreamSink] in terms of [onData], +/// An abstract class that implements [StreamSink] in terms of [onAdd], /// [onError], and [onClose] methods. /// /// This takes care of ensuring that events can't be added after [close] is -/// called or during a call to [onStream]. +/// called or during a call to [addStream]. @Deprecated('Will be removed in the next major release') abstract class StreamSinkBase extends EventSinkBase implements StreamSink { @@ -97,23 +97,23 @@ abstract class StreamSinkBase extends EventSinkBase } } -/// An abstract class that implements `dart:io`'s [IOSink]'s API in terms of -/// [onData], [onError], [onClose], and [onFlush] methods. +/// An abstract class that implements `dart:io`'s `IOSink`'s API in terms of +/// [onAdd], [onError], [onClose], and [onFlush] methods. /// -/// Because [IOSink] is defined in `dart:io`, this can't officially implement +/// Because `IOSink` is defined in `dart:io`, this can't officially implement /// it. However, it's designed to match its API exactly so that subclasses can -/// implement [IOSink] without any additional modifications. +/// implement `IOSink` without any additional modifications. /// /// This takes care of ensuring that events can't be added after [close] is -/// called or during a call to [onStream]. +/// called or during a call to [addStream]. @Deprecated('Will be removed in the next major release') abstract class IOSinkBase extends StreamSinkBase> { - /// See [IOSink.encoding] from `dart:io`. + /// See `IOSink.encoding` from `dart:io`. Encoding encoding; IOSinkBase([this.encoding = utf8]); - /// See [IOSink.flush] from `dart:io`. + /// See `IOSink.flush` from `dart:io`. /// /// Because this base class doesn't do any buffering of its own, [flush] /// always completes immediately. @@ -137,14 +137,14 @@ abstract class IOSinkBase extends StreamSinkBase> { @visibleForOverriding Future onFlush(); - /// See [IOSink.write] from `dart:io`. + /// See [StringSink.write]. void write(Object? object) { var string = object.toString(); if (string.isEmpty) return; add(encoding.encode(string)); } - /// See [IOSink.writeAll] from `dart:io`. + /// See [StringSink.writeAll]. void writeAll(Iterable objects, [String separator = '']) { var first = true; for (var object in objects) { @@ -158,13 +158,13 @@ abstract class IOSinkBase extends StreamSinkBase> { } } - /// See [IOSink.writeln] from `dart:io`. + /// See [StringSink.writeln]. void writeln([Object? object = '']) { write(object); write('\n'); } - /// See [IOSink.writeCharCode] from `dart:io`. + /// See [StringSink.writeCharCode]. void writeCharCode(int charCode) { write(String.fromCharCode(charCode)); } diff --git a/pkgs/async/lib/src/stream_extensions.dart b/pkgs/async/lib/src/stream_extensions.dart index f8a180f5..13811e4c 100644 --- a/pkgs/async/lib/src/stream_extensions.dart +++ b/pkgs/async/lib/src/stream_extensions.dart @@ -6,10 +6,10 @@ import 'dart:async'; /// Utility extensions on [Stream]. extension StreamExtensions on Stream { - /// Creates a stream whose elements are contiguous slices of [this]. + /// Creates a stream whose elements are contiguous slices of `this`. /// /// Each slice is [length] elements long, except for the last one which may be - /// shorter if [this] emits too few elements. Each slice begins after the + /// shorter if `this` emits too few elements. Each slice begins after the /// last one ends. /// /// For example, `Stream.fromIterable([1, 2, 3, 4, 5]).slices(2)` emits diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 514c3a4a..78912c9f 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -14,7 +14,7 @@ import 'package:collection/collection.dart'; /// this means that events emitted by broadcast streams will be dropped until /// [stream] has a listener.** /// -/// If the `StreamGroup` is constructed using [new StreamGroup], [stream] will +/// If the `StreamGroup` is constructed using [StreamGroup.new], [stream] will /// be single-subscription. In this case, if [stream] is paused or canceled, all /// streams in the group will likewise be paused or canceled, respectively. /// @@ -272,7 +272,7 @@ class StreamGroup implements Sink> { /// Starts actively forwarding events from [stream] to [_controller]. /// - /// This will pause the resulting subscription if [this] is paused. + /// This will pause the resulting subscription if `this` is paused. StreamSubscription _listenToStream(Stream stream) { var subscription = stream.listen(_controller.add, onError: _controller.addError, onDone: () => remove(stream)); diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index f9902b96..f7ab8bad 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -9,9 +9,9 @@ import 'package:collection/collection.dart'; import 'cancelable_operation.dart'; import 'result/result.dart'; -import 'subscription_stream.dart'; import 'stream_completer.dart'; import 'stream_splitter.dart'; +import 'subscription_stream.dart'; /// An asynchronous pull-based interface for accessing stream events. /// @@ -573,7 +573,7 @@ class StreamQueueTransaction { /// /// The parent queue's position is updated to be the same as [queue]'s. /// Further requests on all queues created by this transaction, including - /// [queue], will complete as though [cancel] were called with `immediate: + /// [queue], will complete as though `cancel` were called with `immediate: /// true`. /// /// Throws a [StateError] if [commit] or [reject] have already been called, or @@ -600,7 +600,7 @@ class StreamQueueTransaction { /// /// The parent will continue as though [StreamQueue.startTransaction] hadn't /// been called. Further requests on all queues created by this transaction - /// will complete as though [cancel] were called with `immediate: true`. + /// will complete as though `cancel` were called with `immediate: true`. /// /// Throws a [StateError] if [commit] or [reject] have already been called. void reject() { @@ -625,7 +625,7 @@ class StreamQueueTransaction { } } - /// Throws a [StateError] if [accept] or [reject] has already been called. + /// Throws a [StateError] if [commit] or [reject] has already been called. void _assertActive() { if (_committed) { throw StateError('This transaction has already been accepted.'); @@ -641,20 +641,20 @@ class StreamQueueTransaction { /// an `_EventRequest` object in the request queue. /// /// Events from the source stream are sent to the first request in the -/// queue until it reports itself as [isComplete]. +/// queue until it reports itself as `isComplete`. /// /// When the first request in the queue `isComplete`, either when becoming -/// the first request or after receiving an event, its [close] methods is +/// the first request or after receiving an event, its `close` methods is /// called. /// -/// The [close] method is also called immediately when the source stream +/// The `close` method is also called immediately when the source stream /// is done. abstract class _EventRequest { /// Handle available events. /// /// The available events are provided as a queue. The `update` function /// should only remove events from the front of the event queue, e.g., - /// using [removeFirst]. + /// using `removeFirst`. /// /// Returns `true` if the request is completed, or `false` if it needs /// more events. @@ -732,7 +732,7 @@ class _SkipRequest implements _EventRequest { /// Number of remaining events to skip. /// - /// The request [isComplete] when the values reaches zero. + /// The request `isComplete` when the values reaches zero. /// /// Decremented when an event is seen. /// Set to zero when an error is seen since errors abort the skip request. @@ -774,7 +774,7 @@ abstract class _ListRequest implements _EventRequest { /// Number of events to capture. /// - /// The request [isComplete] when the length of [_list] reaches + /// The request `isComplete` when the length of [_list] reaches /// this value. final int _eventsToTake; @@ -786,7 +786,7 @@ abstract class _ListRequest implements _EventRequest { /// Request for a [StreamQueue.take] call. class _TakeRequest extends _ListRequest { - _TakeRequest(int eventsToTake) : super(eventsToTake); + _TakeRequest(super.eventsToTake); @override bool update(QueueList> events, bool isDone) { @@ -810,7 +810,7 @@ class _TakeRequest extends _ListRequest { /// Request for a [StreamQueue.lookAhead] call. class _LookAheadRequest extends _ListRequest { - _LookAheadRequest(int eventsToTake) : super(eventsToTake); + _LookAheadRequest(super.eventsToTake); @override bool update(QueueList> events, bool isDone) { diff --git a/pkgs/async/lib/src/stream_sink_completer.dart b/pkgs/async/lib/src/stream_sink_completer.dart index 10e549d8..dc2f6df3 100644 --- a/pkgs/async/lib/src/stream_sink_completer.dart +++ b/pkgs/async/lib/src/stream_sink_completer.dart @@ -12,7 +12,8 @@ import 'null_stream_sink.dart'; /// until [setDestinationSink] is called, the events will be buffered. /// /// The same effect can be achieved by using a [StreamController] and adding it -/// to the sink using [Sink.addStream] when the destination sink is ready. This +/// to the sink using [StreamConsumer.addStream] when the destination sink is +/// ready. This /// class attempts to shortcut some of the overhead when possible. For example, /// if the [sink] only has events added after the destination sink has been set, /// those events are added directly to the sink. @@ -35,7 +36,7 @@ class StreamSinkCompleter { /// to the result of the future when the future completes. /// /// If the future completes with an error, the returned sink will instead - /// be closed. Its [Sink.done] future will contain the error. + /// be closed. Its [StreamSink.done] future will contain the error. static StreamSink fromFuture(Future> sinkFuture) { var completer = StreamSinkCompleter(); sinkFuture.then(completer.setDestinationSink, onError: completer.setError); @@ -64,7 +65,8 @@ class StreamSinkCompleter { _sink._setDestinationSink(destinationSink); } - /// Completes this to a closed sink whose [Sink.done] future emits [error]. + /// Completes this to a closed sink whose [StreamSink.done] future emits + /// [error]. /// /// This is useful when the process of loading the sink fails. /// @@ -118,7 +120,7 @@ class _CompleterSink implements StreamSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { if (_canSendDirectly) { _destinationSink!.addError(error, stackTrace); } else { diff --git a/pkgs/async/lib/src/stream_sink_extensions.dart b/pkgs/async/lib/src/stream_sink_extensions.dart index ed43341c..a82cfe1c 100644 --- a/pkgs/async/lib/src/stream_sink_extensions.dart +++ b/pkgs/async/lib/src/stream_sink_extensions.dart @@ -13,7 +13,7 @@ extension StreamSinkExtensions on StreamSink { StreamSink transform(StreamSinkTransformer transformer) => transformer.bind(this); - /// Returns a [StreamSink] that forwards to [this] but rejects errors. + /// Returns a [StreamSink] that forwards to `this` but rejects errors. /// /// If an error is passed (either by [addError] or [addStream]), the /// underlying sink will be closed and the error will be forwarded to the diff --git a/pkgs/async/lib/src/stream_sink_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer.dart index d42d19ce..c1ed7478 100644 --- a/pkgs/async/lib/src/stream_sink_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer.dart @@ -53,7 +53,7 @@ abstract class StreamSinkTransformer { /// This means that calls to [StreamSink.add] on the returned sink may throw a /// [TypeError] if the argument type doesn't match the reified type of the /// sink. - @Deprecated("Will be removed in future version") + @Deprecated('Will be removed in future version') // TODO remove TypeSafeStreamSinkTransformer static StreamSinkTransformer typed( StreamSinkTransformer transformer) => diff --git a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart index d6f188ec..496c7ca4 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/handler_transformer.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import '../stream_sink_transformer.dart'; import '../delegate/stream_sink.dart'; +import '../stream_sink_transformer.dart'; /// The type of the callback for handling data events. typedef HandleData = void Function(S data, EventSink sink); @@ -63,7 +63,7 @@ class _HandlerSink implements StreamSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { var handleError = _transformer._handleError; if (handleError == null) { _inner.addError(error, stackTrace); @@ -98,7 +98,7 @@ class _HandlerSink implements StreamSink { /// call [close], they don't leave any dangling [Future]s behind that might emit /// unhandleable errors. class _SafeCloseSink extends DelegatingStreamSink { - _SafeCloseSink(StreamSink inner) : super(inner); + _SafeCloseSink(super.inner); @override Future close() => super.close().catchError((_) {}); diff --git a/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart b/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart index 7e15d265..6d077f46 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/reject_errors.dart @@ -63,7 +63,7 @@ class RejectErrorsSink implements StreamSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { if (_closed) throw StateError('Cannot add event after closing.'); if (_inAddStream) { throw StateError('Cannot add event while adding stream.'); diff --git a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart index 2afcbff8..b30e8ad7 100644 --- a/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart +++ b/pkgs/async/lib/src/stream_sink_transformer/stream_transformer_wrapper.dart @@ -50,7 +50,7 @@ class _StreamTransformerWrapperSink implements StreamSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { _controller.addError(error, stackTrace); } diff --git a/pkgs/async/lib/src/stream_zip.dart b/pkgs/async/lib/src/stream_zip.dart index 506bf6d9..f5b8296d 100644 --- a/pkgs/async/lib/src/stream_zip.dart +++ b/pkgs/async/lib/src/stream_zip.dart @@ -9,7 +9,7 @@ import 'dart:async'; /// This emits lists of collected values from each input stream. The first list /// contains the first value emitted by each stream, the second contains the /// second value, and so on. The lists have the same ordering as the iterable -/// passed to [new StreamZip]. +/// passed to [StreamZip.new]. /// /// Any errors from any of the streams are forwarded directly to this stream. class StreamZip extends Stream> { diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 2c94e05d..6be88e30 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -68,8 +68,7 @@ class SubscriptionStream extends Stream { /// source subscription on the first error. class _CancelOnErrorSubscriptionWrapper extends DelegatingStreamSubscription { - _CancelOnErrorSubscriptionWrapper(StreamSubscription subscription) - : super(subscription); + _CancelOnErrorSubscriptionWrapper(super.subscription); @override void onError(Function? handleError) { @@ -80,7 +79,7 @@ class _CancelOnErrorSubscriptionWrapper if (handleError is ZoneBinaryCallback) { handleError(error, stackTrace); } else if (handleError != null) { - handleError(error); + (handleError as ZoneUnaryCallback)(error); } }); }); diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index cb6f348e..5ff1cb52 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,11 +1,11 @@ name: async -version: 2.10.0-dev +version: 2.10.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async environment: - sdk: ">=2.14.0 <3.0.0" + sdk: ">=2.18.0 <3.0.0" dependencies: collection: ^1.15.0 @@ -13,6 +13,6 @@ dependencies: dev_dependencies: fake_async: ^1.2.0 - lints: ^1.0.0 + lints: ^2.0.0 stack_trace: ^1.10.0 test: ^1.16.0 diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 4948e530..77bda7c5 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -45,7 +45,7 @@ void main() { // No actual caching is done, just avoid duplicate requests. cache = AsyncCache.ephemeral(); - var fetched = cache.fetch(() async => "first"); + var fetched = cache.fetch(() async => 'first'); expect(fetched, completion('first')); expect( cache.fetch(expectAsync0(() async => fail('not called'), count: 0)), @@ -111,9 +111,7 @@ void main() { yield '3'; }).toList(); expect( - await cache - .fetchStream(expectAsync0(() => Stream.empty(), count: 0)) - .toList(), + await cache.fetchStream(expectAsync0(Stream.empty, count: 0)).toList(), ['1', '2', '3']); }); diff --git a/pkgs/async/test/byte_collection_test.dart b/pkgs/async/test/byte_collection_test.dart index 7f2ecc64..67f319b0 100644 --- a/pkgs/async/test/byte_collection_test.dart +++ b/pkgs/async/test/byte_collection_test.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'package:test/test.dart'; import 'package:async/async.dart'; +import 'package:test/test.dart'; void main() { group('collectBytes', () { diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index f073e80f..63ba2c34 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -591,7 +591,7 @@ void main() { var called = false; onValue = expectAsync1((_) { called = true; - fail("onValue unreachable"); + fail('onValue unreachable'); }, count: 0); await runThen().cancel(); @@ -604,11 +604,11 @@ void main() { var called = false; onError = expectAsync2((_, __) { called = true; - fail("onError unreachable"); + fail('onError unreachable'); }, count: 0); await runThen().cancel(); - originalCompleter.completeError("Error", StackTrace.empty); + originalCompleter.completeError('Error', StackTrace.empty); await flushMicrotasks(); expect(called, false); }); @@ -617,7 +617,7 @@ void main() { var called = false; onCancel = expectAsync0(() { called = true; - fail("onCancel unreachable"); + fail('onCancel unreachable'); }, count: 0); await runThen().cancel(); @@ -844,7 +844,7 @@ void main() { onError = expectAsync3((_, __, ___) {}, count: 0); await runThenOperation().cancel(); - originalCompleter.completeError("Error", StackTrace.empty); + originalCompleter.completeError('Error', StackTrace.empty); }); test('onCancel callback not called after cancel', () async { @@ -893,11 +893,11 @@ void main() { }); test('throws the first error to complete', () { - completer1.completeError("error 1"); - completer2.completeError("error 2"); - completer3.completeError("error 3"); + completer1.completeError('error 1'); + completer2.completeError('error 2'); + completer3.completeError('error 3'); - expect(operation.value, throwsA("error 1")); + expect(operation.value, throwsA('error 1')); }); test('cancels any completers that haven\'t completed', () async { diff --git a/pkgs/async/test/chunked_stream_reader.dart b/pkgs/async/test/chunked_stream_reader.dart index d80cf837..b4dd4f0a 100644 --- a/pkgs/async/test/chunked_stream_reader.dart +++ b/pkgs/async/test/chunked_stream_reader.dart @@ -5,8 +5,8 @@ import 'dart:async'; import 'dart:typed_data'; -import 'package:test/test.dart'; import 'package:async/async.dart'; +import 'package:test/test.dart'; void main() { test('readChunk() chunk by chunk', () async { diff --git a/pkgs/async/test/io_sink_impl.dart b/pkgs/async/test/io_sink_impl.dart index 832eb715..8fbf1c51 100644 --- a/pkgs/async/test/io_sink_impl.dart +++ b/pkgs/async/test/io_sink_impl.dart @@ -2,7 +2,7 @@ // 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. -@Deprecated("Tests deprecated functionality") +@Deprecated('Tests deprecated functionality') library io_sink_impl; import 'dart:io'; diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index 32c6b142..cde1928f 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -83,7 +83,7 @@ void main() { }); test("a lazy stream can't be listened to multiple times", () { - var stream = LazyStream(expectAsync0(() => Stream.empty())); + var stream = LazyStream(expectAsync0(Stream.empty)); expect(stream.isBroadcast, isFalse); stream.listen(null); diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index fb528360..25c57759 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -14,7 +14,7 @@ final someStack = StackTrace.current; Result res(int n) => Result.value(n); -Result err(n) => ErrorResult('$n', someStack); +Result err(int n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of futures. Iterable> futures(int count, diff --git a/pkgs/async/test/result/result_flattenAll_test.dart b/pkgs/async/test/result/result_flattenAll_test.dart index 2521e9e2..0d2b9634 100644 --- a/pkgs/async/test/result/result_flattenAll_test.dart +++ b/pkgs/async/test/result/result_flattenAll_test.dart @@ -9,7 +9,7 @@ import 'package:test/test.dart'; final someStack = StackTrace.current; Result res(T n) => Result.value(n); -Result err(n) => ErrorResult('$n', someStack); +Result err(int n) => ErrorResult('$n', someStack); /// Helper function creating an iterable of results. Iterable> results(int count, diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index 154785ba..568a8910 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -335,7 +335,7 @@ class TestSink implements EventSink { } @override - void addError(error, [StackTrace? stack]) { + void addError(Object error, [StackTrace? stack]) { onError(error, stack ?? StackTrace.fromString('')); } diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart index 33958dd0..1f453946 100644 --- a/pkgs/async/test/sink_base_test.dart +++ b/pkgs/async/test/sink_base_test.dart @@ -2,15 +2,14 @@ // 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. -@Deprecated("Tests deprecated functionality") +@Deprecated('Tests deprecated functionality') library sink_base_test; import 'dart:async'; import 'dart:convert'; -import 'package:test/test.dart'; - import 'package:async/async.dart'; +import 'package:test/test.dart'; const int letterA = 0x41; @@ -313,15 +312,13 @@ void main() { }); test('does nothing after close() is called', () { - var sink = - _IOSink(onFlush: expectAsync0(() => Future.value(), count: 0)); + var sink = _IOSink(onFlush: expectAsync0(Future.value, count: 0)); expect(sink.close(), completes); expect(sink.flush(), completes); }); test("can't be called during addStream()", () { - var sink = - _IOSink(onFlush: expectAsync0(() => Future.value(), count: 0)); + var sink = _IOSink(onFlush: expectAsync0(Future.value, count: 0)); sink.addStream(StreamController>().stream); expect(() => sink.flush(), throwsStateError); }); @@ -385,7 +382,7 @@ class _IOSink extends IOSinkBase { : _onAdd = onAdd ?? ((_) {}), _onError = onError ?? ((_, [__]) {}), _onClose = onClose ?? (() {}), - _onFlush = onFlush ?? (() => Future.value()), + _onFlush = onFlush ?? (Future.value), super(encoding); @override diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index 5ccf44e0..eaeb7192 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -285,7 +285,7 @@ void main() { await flushMicrotasks(); expect(lastEvent, '2'); subscription.onData((value) => lastEvent = -value); - subscription.onError((value) => lastEvent = '${-value}'); + subscription.onError((value) => lastEvent = '${-(value as int)}'); controller.add(1); await flushMicrotasks(); expect(lastEvent, -1); diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 256056e6..5c684f30 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -495,11 +495,11 @@ void main() { group('regardless of type', () { group('single-subscription', () { - regardlessOfType(() => StreamGroup()); + regardlessOfType(StreamGroup.new); }); group('broadcast', () { - regardlessOfType(() => StreamGroup.broadcast()); + regardlessOfType(StreamGroup.broadcast); }); }); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 1d45a8c2..626eb16d 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -1124,7 +1124,7 @@ void main() { // Test expecting [startIndex .. startIndex + 9] as events using // `next`. - void nextTest(startIndex) { + void nextTest(int startIndex) { for (var i = 0; i < 10; i++) { expect(events.next, completion(startIndex + i)); } @@ -1137,7 +1137,7 @@ void main() { // Test expecting [startIndex .. startIndex + 9] as events using // `take(10)`. - void takeTest(startIndex) { + void takeTest(int startIndex) { expect(events.take(10), completion(List.generate(10, (i) => startIndex + i))); } diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 8be0572d..67c6c04b 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -21,7 +21,7 @@ void main() { test('subscription stream after two events', () async { var stream = createStream(); var skips = 0; - var completer = Completer(); + var completer = Completer>(); late StreamSubscription subscription; subscription = stream.listen((value) { ++skips; diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index b7e64e2f..2fa1b92c 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -21,7 +21,7 @@ OptionalArgAction unreachable(String name) => /// A matcher that runs a callback in its own zone and asserts that that zone /// emits an error that matches [matcher]. -Matcher throwsZoned(matcher) => predicate((void Function() callback) { +Matcher throwsZoned(Matcher matcher) => predicate((void Function() callback) { var firstError = true; runZonedGuarded( callback, @@ -48,7 +48,8 @@ final throwsTypeError = throwsA(TypeMatcher()); /// Can be used to test cases where a stream should not be used. class UnusableStream extends Stream { @override - StreamSubscription listen(onData, {onError, onDone, cancelOnError}) { + StreamSubscription listen(void Function(T event)? onData, + {Function? onError, void Function()? onDone, bool? cancelOnError}) { throw UnimplementedError('Gotcha!'); } } @@ -67,7 +68,7 @@ class CompleterStreamSink implements StreamSink { @override void add(T event) {} @override - void addError(error, [StackTrace? stackTrace]) {} + void addError(Object error, [StackTrace? stackTrace]) {} @override Future addStream(Stream stream) async {} @override @@ -103,7 +104,7 @@ class TestSink implements StreamSink { } @override - void addError(error, [StackTrace? stackTrace]) { + void addError(Object error, [StackTrace? stackTrace]) { results.add(Result.error(error, stackTrace)); } From 7f065ca8ef75b6bd34592aee542d75675883bdec Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 20 Oct 2022 12:45:11 -0700 Subject: [PATCH 220/260] Nit: fix analysis_options (dart-lang/async#224) --- pkgs/async/analysis_options.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index ca9be773..60b769dd 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -3,9 +3,7 @@ include: package:lints/recommended.yaml analyzer: language: - #strict-casts: true - #strict-inference: true - #strict-raw-types: true + strict-casts: true linter: rules: From 59f655ca35ab8c028092b753dd2716142be5cb80 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 9 Nov 2022 09:20:15 -0800 Subject: [PATCH 221/260] Add CancelableOperation.fromValue (dart-lang/async#225) This is useful in tests which stub behavior for an API which returns `CancelableOperation`. It has more clear semantics and is shorter than `CancelableOperation.fromFuture(Future.value(...))`. Take a `T` instead of a more general `FutureOr` to satisfy both cases because it allows simplifying the API with no `onCancel` support. --- pkgs/async/CHANGELOG.md | 1 + pkgs/async/lib/src/cancelable_operation.dart | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 1b3144ce..d6dd2aaa 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -3,6 +3,7 @@ * Add `CancelableOperation.thenOperation` which gives more flexibility to complete the resulting operation. * Add `CancelableCompleter.completeOperation`. +* Add `CancelableOperation.fromValue`. * Require Dart 2.18 ## 2.9.0 diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 443ee78a..09997e0f 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -25,7 +25,7 @@ class CancelableOperation { /// If [onCancel] returns a [Future], it will be returned by [cancel]. /// /// The [onCancel] funcion will be called synchronously - /// when the new operation is canceled, and will be called at most once.\ + /// when the new operation is canceled, and will be called at most once. /// /// Calling this constructor is equivalent to creating a /// [CancelableCompleter] and completing it with [result]. @@ -33,6 +33,15 @@ class CancelableOperation { {FutureOr Function()? onCancel}) => (CancelableCompleter(onCancel: onCancel)..complete(result)).operation; + /// Creates a [CancelableOperation] which completes to [value]. + /// + /// Canceling this operation does nothing. + /// + /// Calling this constructor is equivalent to creating a + /// [CancelableCompleter] and completing it with [value]. + factory CancelableOperation.fromValue(T value) => + (CancelableCompleter()..complete(value)).operation; + /// Creates a [CancelableOperation] wrapping [subscription]. /// /// This overrides [StreamSubscription.onDone] and From c51a3737e0ed51af65d066d0f10cc349db7624bc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 09:27:57 -0800 Subject: [PATCH 222/260] Bump actions/checkout from 3.1.0 to 3.2.0 (dart-lang/async#227) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index d257b9f9..265a4f31 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.0, dev] steps: - - uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 + - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b - uses: dart-lang/setup-dart@6a218f2413a3e78e9087f638a238f6b40893203d with: sdk: ${{ matrix.sdk }} From 9ad4cf7023319bc21099e7d845b0aab75202b49c Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Mon, 23 Jan 2023 10:13:59 -0800 Subject: [PATCH 223/260] update changelog and pubspec version (dart-lang/async#228) --- pkgs/async/CHANGELOG.md | 5 ++++- pkgs/async/README.md | 2 ++ pkgs/async/pubspec.yaml | 3 +-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index d6dd2aaa..839eb91f 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,9 +1,12 @@ +## 2.11.0-dev + +* Add `CancelableOperation.fromValue`. + ## 2.10.0 * Add `CancelableOperation.thenOperation` which gives more flexibility to complete the resulting operation. * Add `CancelableCompleter.completeOperation`. -* Add `CancelableOperation.fromValue`. * Require Dart 2.18 ## 2.9.0 diff --git a/pkgs/async/README.md b/pkgs/async/README.md index b25b7407..ce3c5117 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -5,6 +5,8 @@ Contains utility classes in the style of `dart:async` to work with asynchronous computations. +## Package API + * The [`AsyncCache`][AsyncCache] class allows expensive asynchronous computations values to be cached for a period of time. diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 5ff1cb52..7ea95f8a 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,6 +1,5 @@ name: async -version: 2.10.0 - +version: 2.11.0-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async From b443bb16995e58f4c903ffa3853b6a0441045446 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 27 Jan 2023 14:07:02 -0800 Subject: [PATCH 224/260] blast_repo fixes (dart-lang/async#231) auto-publish --- pkgs/async/.github/workflows/publish.yaml | 14 ++++++++++++++ pkgs/async/README.md | 5 +++++ 2 files changed, 19 insertions(+) create mode 100644 pkgs/async/.github/workflows/publish.yaml diff --git a/pkgs/async/.github/workflows/publish.yaml b/pkgs/async/.github/workflows/publish.yaml new file mode 100644 index 00000000..fcb7ccb8 --- /dev/null +++ b/pkgs/async/.github/workflows/publish.yaml @@ -0,0 +1,14 @@ +# 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 diff --git a/pkgs/async/README.md b/pkgs/async/README.md index ce3c5117..be9ed20d 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -92,3 +92,8 @@ computations. [StreamZip]: https://pub.dev/documentation/async/latest/async/StreamZip-class.html [SubscriptionStream]: https://pub.dev/documentation/async/latest/async/SubscriptionStream-class.html [typedStreamTransformer]: https://pub.dev/documentation/async/latest/async/typedStreamTransformer.html + +## Publishing automation + +For information about our publishing automation and release process, see +https://github.com/dart-lang/ecosystem/wiki/Publishing-automation. From 9015f5cf78a47ec52768b8bc56f864a9e978b5f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 08:36:16 -0800 Subject: [PATCH 225/260] Bump dart-lang/setup-dart from 1.3 to 1.4 (dart-lang/async#233) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 265a4f31..54fc2c89 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.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 2f756b5e8f96b276cdc1d2574787e8905df44732 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 08:38:37 -0800 Subject: [PATCH 226/260] Bump actions/checkout from 3.2.0 to 3.3.0 (dart-lang/async#234) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 54fc2c89..729b3139 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.0, dev] steps: - - uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 with: sdk: ${{ matrix.sdk }} From 78faa62464fa8719518d616dc4ad78cd37aebeed Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 2 Mar 2023 00:04:21 -0800 Subject: [PATCH 227/260] Add `StreamExtensions.listenAndBuffer()` (dart-lang/async#235) The immediate motivation for this is to buffer stderr from a subprocess until we learn whether the subprocess completed successfully or not. It's important to consume the output eagerly so the process doesn't deadlock after saturating the OS and/or `dart:io` buffers, but we don't want to print it at all if the process completes successfully. --- pkgs/async/CHANGELOG.md | 4 +- pkgs/async/lib/src/stream_extensions.dart | 24 ++++++ pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/stream_extensions_test.dart | 90 +++++++++++++++++++++ 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 839eb91f..573d87fb 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,8 @@ -## 2.11.0-dev +## 2.11.0 * Add `CancelableOperation.fromValue`. +* Add `StreamExtensions.listenAndBuffer`, which buffers events from a stream + before it has a listener. ## 2.10.0 diff --git a/pkgs/async/lib/src/stream_extensions.dart b/pkgs/async/lib/src/stream_extensions.dart index 13811e4c..4ba9254f 100644 --- a/pkgs/async/lib/src/stream_extensions.dart +++ b/pkgs/async/lib/src/stream_extensions.dart @@ -54,4 +54,28 @@ extension StreamExtensions on Stream { }); return completer.future; } + + /// Eagerly listens to this stream and buffers events until needed. + /// + /// The returned stream will emit the same events as this stream, starting + /// from when this method is called. The events are delayed until the returned + /// stream is listened to, at which point all buffered events will be emitted + /// in order, and then further events from this stream will be emitted as they + /// arrive. + /// + /// The buffer will retain all events until the returned stream is listened + /// to, so if the stream can emit arbitrary amounts of data, callers should be + /// careful to listen to the stream eventually or call + /// `stream.listen(null).cancel()` to discard the buffered data if it becomes + /// clear that the data isn't not needed. + Stream listenAndBuffer() { + var controller = StreamController(sync: true); + var subscription = listen(controller.add, + onError: controller.addError, onDone: controller.close); + controller + ..onPause = subscription.pause + ..onResume = subscription.resume + ..onCancel = subscription.cancel; + return controller.stream; + } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 7ea95f8a..61dd362b 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.11.0-dev +version: 2.11.0 description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/stream_extensions_test.dart b/pkgs/async/test/stream_extensions_test.dart index 2118ae75..c9229232 100644 --- a/pkgs/async/test/stream_extensions_test.dart +++ b/pkgs/async/test/stream_extensions_test.dart @@ -92,4 +92,94 @@ void main() { expect(isCancelled, isTrue); }); }); + + group('.listenAndBuffer', () { + test('emits events added before the listenAndBuffer is listened', () async { + var controller = StreamController() + ..add(1) + ..add(2) + ..add(3) + ..close(); + var stream = controller.stream.listenAndBuffer(); + await pumpEventQueue(); + + expectLater(stream, emitsInOrder([1, 2, 3, emitsDone])); + }); + + test('emits events added after the listenAndBuffer is listened', () async { + var controller = StreamController(); + var stream = controller.stream.listenAndBuffer(); + expectLater(stream, emitsInOrder([1, 2, 3, emitsDone])); + await pumpEventQueue(); + + controller + ..add(1) + ..add(2) + ..add(3) + ..close(); + }); + + test('emits events added before and after the listenAndBuffer is listened', + () async { + var controller = StreamController() + ..add(1) + ..add(2) + ..add(3); + var stream = controller.stream.listenAndBuffer(); + expectLater(stream, emitsInOrder([1, 2, 3, 4, 5, 6, emitsDone])); + await pumpEventQueue(); + + controller + ..add(4) + ..add(5) + ..add(6) + ..close(); + }); + + test('listens as soon as listenAndBuffer() is called', () async { + var listened = false; + var controller = StreamController(onListen: () { + listened = true; + }); + controller.stream.listenAndBuffer(); + expect(listened, isTrue); + }); + + test('forwards pause and resume', () async { + var controller = StreamController(); + var stream = controller.stream.listenAndBuffer(); + expect(controller.isPaused, isFalse); + var subscription = stream.listen(null); + expect(controller.isPaused, isFalse); + subscription.pause(); + expect(controller.isPaused, isTrue); + subscription.resume(); + expect(controller.isPaused, isFalse); + }); + + test('forwards cancel', () async { + var completer = Completer(); + var canceled = false; + var controller = StreamController(onCancel: () { + canceled = true; + return completer.future; + }); + var stream = controller.stream.listenAndBuffer(); + expect(canceled, isFalse); + var subscription = stream.listen(null); + expect(canceled, isFalse); + + var cancelCompleted = false; + subscription.cancel().then((_) { + cancelCompleted = true; + }); + expect(canceled, isTrue); + await pumpEventQueue(); + expect(cancelCompleted, isFalse); + + completer.complete(); + await pumpEventQueue(); + expect(cancelCompleted, isTrue); + }); + }); } From a20f11cc178ee53912fa4b07d52bb6795e3aad7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:36:51 -0700 Subject: [PATCH 228/260] Bump dart-lang/setup-dart from 1.4.0 to 1.5.0 (dart-lang/async#237) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 729b3139..9df24041 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.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 d33b646e15b965bdeb5ab851dc872299f68fba53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 15:57:34 -0700 Subject: [PATCH 229/260] Bump actions/checkout from 3.3.0 to 3.5.0 (dart-lang/async#236) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 9df24041..bc89aa25 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.0, dev] steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From db3aeb8798b6fb67920b832e4f6f4019f68f49fc Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 19 Apr 2023 09:58:46 -0700 Subject: [PATCH 230/260] Regression test for error rejecting transaction (dart-lang/async#230) Towards dart-lang/async#229 Add a skipped test demonstrating the sequence of interaction with a StreamQueueTransaction that leads to a `StateError` during the call to `transaction.reject()`. --- pkgs/async/test/stream_queue_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 626eb16d..7c575c09 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -922,6 +922,25 @@ void main() { await flushMicrotasks(); controller.close(); }); + + test( + 'can reject a transaction where one copy is fully consumed ' + 'in a transaction and a second copy is made', () async { + // Regression test for https://github.com/dart-lang/async/issues/229 + final queue = StreamQueue(Stream.fromIterable([0])); + final transaction = queue.startTransaction(); + + final copy1 = transaction.newQueue(); + final inner1 = copy1.startTransaction(); + final innerCopy1 = inner1.newQueue(); + await innerCopy1.next; + + transaction.newQueue(); + + transaction.reject(); + expect(await queue.next, 0); + expect(await queue.hasNext, isFalse); + }, skip: 'https://github.com/dart-lang/async/issues/229'); }); group('when committed', () { From 55f52223199dcd6140282d7f4b30d8db46f42a47 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 2 May 2023 09:20:15 -0700 Subject: [PATCH 231/260] Synchronously forward chained cancellations (dart-lang/async#239) Add a test which chains through multiple `.then` calls and then cancels the original operation. This test passes with the original code, but starts failing after fixing a bug in `Completer.complete` in the SDK. https://dart-review.googlesource.com/c/sdk/+/258929 - Add a list of extra completers to forward cancellation. In places where the cancellation had been forwarded through a `whenComplete`, add to the list of extra cancellations instead. This allows the Future return by the outer cancellation to wait for the other callbacks to complete before firing. - When setting up forwarding on an already canceled completer, immediately forward the cancellation. - When forwarding cancellations, don't repeatedly call `_cancel()` on a completer which has already been canceled. - In `race()`, disable cancellation propagation to prevent a cyclic cancellation. --- pkgs/async/CHANGELOG.md | 2 + pkgs/async/lib/src/cancelable_operation.dart | 96 +++++++++++++++---- pkgs/async/pubspec.yaml | 2 +- .../async/test/cancelable_operation_test.dart | 11 +++ 4 files changed, 92 insertions(+), 19 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 573d87fb..210c876e 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,3 +1,5 @@ +## 2.12.0-dev + ## 2.11.0 * Add `CancelableOperation.fromValue`. diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 09997e0f..8cc0c675 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -4,6 +4,8 @@ import 'dart:async'; +import 'package:collection/collection.dart'; + /// An asynchronous operation that can be cancelled. /// /// The value of this operation is exposed as [value]. When this operation is @@ -81,7 +83,10 @@ class CancelableOperation { // they're not actually cancelled by this. Future cancelAll() { done = true; - return Future.wait(operations.map((operation) => operation.cancel())); + return Future.wait([ + for (var operation in operations) + if (!operation.isCanceled) operation.cancel() + ]); } var completer = CancelableCompleter(onCancel: cancelAll); @@ -93,7 +98,7 @@ class CancelableOperation { cancelAll() .whenComplete(() => completer.completeError(error, stackTrace)); } - }); + }, propagateCancel: false); } return completer.operation; @@ -224,8 +229,8 @@ class CancelableOperation { onError, FutureOr Function(CancelableCompleter)? onCancel, bool propagateCancel = true}) { - final completer = - CancelableCompleter(onCancel: propagateCancel ? cancel : null); + final completer = CancelableCompleter( + onCancel: propagateCancel ? _cancelIfNotCanceled : null); // if `_completer._inner` completes before `completer` is cancelled // call `onValue` or `onError` with the result, and complete `completer` @@ -258,20 +263,16 @@ class CancelableOperation { try { await onError(error, stack, completer); } catch (error2, stack2) { - completer.completeError( + completer.completeErrorIfPending( error2, identical(error, error2) ? stack : stack2); } }); - _completer._cancelCompleter?.future.whenComplete(onCancel == null - ? completer._cancel - : () async { - if (completer.isCanceled) return; - try { - await onCancel(completer); - } catch (error, stack) { - completer.completeError(error, stack); - } - }); + final cancelForwarder = _CancelForwarder(completer, onCancel); + if (_completer.isCanceled) { + cancelForwarder._forward(); + } else { + (_completer._cancelForwarders ??= []).add(cancelForwarder); + } return completer.operation; } @@ -281,6 +282,8 @@ class CancelableOperation { /// Returns the result of the `onCancel` callback, if one exists. Future cancel() => _completer._cancel(); + Future? _cancelIfNotCanceled() => isCanceled ? null : cancel(); + /// Whether this operation has been canceled before it completed. bool get isCanceled => _completer._isCanceled; @@ -337,11 +340,18 @@ class CancelableCompleter { /// ever complete. /// Set to `null` when [_inner] is completed, because then it's /// guaranteed that this completer will never complete. - Completer? _cancelCompleter = Completer(); + Completer? _cancelCompleter = Completer(); /// The callback to call if the operation is canceled. final FutureOr Function()? _onCancel; + /// Additional cancellations to forward during cancel. + /// + /// When a cancelable operation is chained through `then` or `thenOperation` a + /// cancellation on the original operation will synchronously cancel the + /// chained operations. + List<_CancelForwarder>? _cancelForwarders; + /// Whether [complete] or [completeError] may still be called. /// /// Set to false when calling either. @@ -497,9 +507,59 @@ class CancelableCompleter { if (_inner != null) { _inner = null; - var onCancel = _onCancel; - cancelCompleter.complete(onCancel == null ? null : Future.sync(onCancel)); + cancelCompleter.complete(_invokeCancelCallbacks()); } return cancelCompleter.future; } + + /// Invoke [_onCancel] and forward to other completers in [_cancelForwarders]. + /// + /// Returns the same value as [_onCancel]. Legacy uses may return a value + /// despite the signature having `void` return. + Future _invokeCancelCallbacks() async { + final FutureOr toReturn = _onCancel?.call(); + final isFuture = toReturn is Future; + final cancelFutures = >[ + if (isFuture) toReturn, + ...?_cancelForwarders?.map(_forward).whereNotNull() + ]; + final results = (isFuture && cancelFutures.length == 1) + ? [await toReturn] + : cancelFutures.isNotEmpty + ? await Future.wait(cancelFutures) + : const []; + return isFuture ? results.first : toReturn; + } +} + +class _CancelForwarder { + final CancelableCompleter completer; + final FutureOr Function(CancelableCompleter)? onCancel; + _CancelForwarder(this.completer, this.onCancel); + + Future? _forward() { + if (completer.isCanceled) return null; + final onCancel = this.onCancel; + if (onCancel == null) return completer._cancel(); + try { + final result = onCancel(completer); + if (result is Future) { + return result.catchError(completer.completeErrorIfPending); + } + } catch (error, stack) { + completer.completeErrorIfPending(error, stack); + } + return null; + } +} + +// Helper function to avoid a closure for `List<_CancelForwarder>.map`. +Future? _forward(_CancelForwarder forwarder) => + forwarder._forward(); + +extension on CancelableCompleter { + void completeErrorIfPending(Object error, StackTrace stackTrace) { + if (isCompleted) return; + completeError(error, stackTrace); + } } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 61dd362b..9802f787 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,5 +1,5 @@ name: async -version: 2.11.0 +version: 2.12.0-dev description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 63ba2c34..f72f7e99 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -568,6 +568,17 @@ void main() { expect(operation.value, completion('foo')); expect(operation.isCanceled, false); }); + + test('waits for chained cancellation', () async { + var completer = CancelableCompleter(); + var chainedOperation = completer.operation + .then((_) => Future.delayed(Duration(milliseconds: 1))) + .then((_) => Future.delayed(Duration(milliseconds: 1))); + + await completer.operation.cancel(); + expect(completer.operation.isCanceled, true); + expect(chainedOperation.isCanceled, true); + }); }); group('returned operation canceled', () { From 4f00bd3205aff3302a9399e603e895a4efc41273 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 12:26:47 -0700 Subject: [PATCH 232/260] Bump actions/checkout from 3.5.0 to 3.5.2 (dart-lang/async#240) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index bc89aa25..0acf9e85 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.0, dev] steps: - - uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 + - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 79870a02928b6ffbb72fb52c0f50013e000cefbe Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 12 May 2023 15:08:15 -0700 Subject: [PATCH 233/260] blast_repo fixes (dart-lang/async#242) dependabot --- pkgs/async/.github/dependabot.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/async/.github/dependabot.yml b/pkgs/async/.github/dependabot.yml index 71cdeea3..e802353f 100644 --- a/pkgs/async/.github/dependabot.yml +++ b/pkgs/async/.github/dependabot.yml @@ -4,7 +4,9 @@ version: 2 updates: -- package-ecosystem: "github-actions" - directory: "/" +- package-ecosystem: github-actions + directory: / schedule: - interval: "monthly" + interval: monthly + labels: + - autosubmit From 3ed98e5ee37823da7a34ec56d0f70059f318b9b1 Mon Sep 17 00:00:00 2001 From: Goddchen Date: Wed, 17 May 2023 19:35:25 +0200 Subject: [PATCH 234/260] docs: fix typo in CancelableOperation.fromFuture(...) docs (dart-lang/async#243) --- pkgs/async/lib/src/cancelable_operation.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 8cc0c675..0964d88a 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -26,7 +26,7 @@ class CancelableOperation { /// or error later produced by [result] will be discarded. /// If [onCancel] returns a [Future], it will be returned by [cancel]. /// - /// The [onCancel] funcion will be called synchronously + /// The [onCancel] function will be called synchronously /// when the new operation is canceled, and will be called at most once. /// /// Calling this constructor is equivalent to creating a From 7bd2eb17261c0292b8605e7320da31f774791b06 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 1 Jun 2023 17:54:44 -0700 Subject: [PATCH 235/260] Require Dart 2.19, use latest team lints (dart-lang/async#244) --- pkgs/async/CHANGELOG.md | 4 +++- pkgs/async/analysis_options.yaml | 14 ++++---------- .../lib/src/single_subscription_transformer.dart | 1 + pkgs/async/pubspec.yaml | 6 +++--- pkgs/async/test/cancelable_operation_test.dart | 2 ++ pkgs/async/test/sink_base_test.dart | 2 +- pkgs/async/test/stream_splitter_test.dart | 2 ++ pkgs/async/test/subscription_stream_test.dart | 6 ------ pkgs/async/test/utils.dart | 2 ++ 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index 210c876e..ccda9d89 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,4 +1,6 @@ -## 2.12.0-dev +## 2.12.0-wip + +- Require Dart 2.19 ## 2.11.0 diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 60b769dd..260fdb0b 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,28 +1,22 @@ # https://dart.dev/guides/language/analysis-options -include: package:lints/recommended.yaml +include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: language: strict-casts: true + errors: + only_throw_errors: ignore + unawaited_futures: ignore linter: rules: - - always_declare_return_types - - avoid_dynamic_calls - avoid_unused_constructor_parameters - comment_references - - directives_ordering - - lines_longer_than_80_chars - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - no_runtimeType_toString - - omit_local_variable_types - package_api_docs - prefer_relative_imports - - prefer_single_quotes - test_types_in_equals - - throw_in_finally - - type_annotate_public_apis - - unnecessary_lambdas - use_super_parameters diff --git a/pkgs/async/lib/src/single_subscription_transformer.dart b/pkgs/async/lib/src/single_subscription_transformer.dart index 2e056b2f..ba6f0d2e 100644 --- a/pkgs/async/lib/src/single_subscription_transformer.dart +++ b/pkgs/async/lib/src/single_subscription_transformer.dart @@ -26,6 +26,7 @@ class SingleSubscriptionTransformer extends StreamTransformerBase { // type parameter and avoid this conversion. try { controller.add(value as T); + // ignore: avoid_catching_errors } on TypeError catch (error, stackTrace) { controller.addError(error, stackTrace); } diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 9802f787..dd2046c6 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,17 +1,17 @@ name: async -version: 2.12.0-dev +version: 2.12.0-wip description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async environment: - sdk: ">=2.18.0 <3.0.0" + sdk: '>=2.19.0 <4.0.0' dependencies: collection: ^1.15.0 meta: ^1.1.7 dev_dependencies: + dart_flutter_team_lints: ^1.0.0 fake_async: ^1.2.0 - lints: ^2.0.0 stack_trace: ^1.10.0 test: ^1.16.0 diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index f72f7e99..6d415edd 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -2,6 +2,8 @@ // 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. +// ignore_for_file: unawaited_futures + import 'dart:async'; import 'package:async/async.dart'; diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart index 1f453946..95f51c90 100644 --- a/pkgs/async/test/sink_base_test.dart +++ b/pkgs/async/test/sink_base_test.dart @@ -382,7 +382,7 @@ class _IOSink extends IOSinkBase { : _onAdd = onAdd ?? ((_) {}), _onError = onError ?? ((_, [__]) {}), _onClose = onClose ?? (() {}), - _onFlush = onFlush ?? (Future.value), + _onFlush = onFlush ?? Future.value, super(encoding); @override diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 34931932..43fe3922 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -2,6 +2,8 @@ // 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. +// ignore_for_file: unawaited_futures + import 'dart:async'; import 'package:async/async.dart'; diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index 67c6c04b..ea626b12 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -180,9 +180,3 @@ Stream createErrorStream([Completer? onCancel]) async* { } } } - -Stream createLongStream() async* { - for (var i = 0; i < 200; i++) { - yield i; - } -} diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index 2fa1b92c..fd87a4ef 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. /// Helper utilities for testing. +library; + import 'dart:async'; import 'package:async/async.dart'; From db823ea8d482ca26af4e3395968d4206e1c5b54f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 1 Jun 2023 18:09:06 -0700 Subject: [PATCH 236/260] Update testing SDK in CI (dart-lang/async#245) --- pkgs/async/.github/workflows/test-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 0acf9e85..310e9aae 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.18.0, dev] + sdk: [2.19.0, dev] steps: - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f From a78ab0fe7dc81b5489151fcdd310f429bd536588 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 1 Jul 2023 11:38:37 +0000 Subject: [PATCH 237/260] Bump actions/checkout from 3.5.2 to 3.5.3 (dart-lang/async#247) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 310e9aae..9cc76737 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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: [2.19.0, dev] steps: - - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab + - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 3e824a588458fd92abd07462c10b4dd4fe3d5a15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Sep 2023 11:16:49 +0000 Subject: [PATCH 238/260] Bump actions/checkout from 3.5.3 to 3.6.0 (dart-lang/async#250) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 9cc76737..90e3d7b7 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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: [2.19.0, dev] steps: - - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 - uses: dart-lang/setup-dart@d6a63dab3335f427404425de0fbfed4686d93c4f with: sdk: ${{ matrix.sdk }} From 9021c853aef4464a5202889f7cdd2632558136c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 11:11:04 +0000 Subject: [PATCH 239/260] Bump dart-lang/setup-dart from 1.5.0 to 1.5.1 (dart-lang/async#252) 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/async#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 90e3d7b7..6290a788 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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: [2.19.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 3570335c494bb30eab8ed318a10419bdc5703614 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Oct 2023 11:14:38 +0000 Subject: [PATCH 240/260] Bump actions/checkout from 3.6.0 to 4.1.0 (dart-lang/async#253) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 6290a788..1cf12fb3 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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: [2.19.0, dev] steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From d7e9a67a3ff9ac0dfca594b11ed17c6354ff6872 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:52:05 +0000 Subject: [PATCH 241/260] Bump actions/checkout from 4.1.0 to 4.1.1 (dart-lang/async#254) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 1cf12fb3..b6ab9cbf 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} @@ -49,7 +49,7 @@ jobs: os: [ubuntu-latest] sdk: [2.19.0, dev] steps: - - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 with: sdk: ${{ matrix.sdk }} From 736d543cf7434f536dc1cd831c0384d1a420e7d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:56:05 +0000 Subject: [PATCH 242/260] Bump dart-lang/setup-dart from 1.5.1 to 1.6.0 (dart-lang/async#255) 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/async#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index b6ab9cbf..a3cc511e 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/async/.github/workflows/test-package.yml @@ -23,7 +23,7 @@ jobs: sdk: [dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install @@ -50,7 +50,7 @@ jobs: sdk: [2.19.0, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - - uses: dart-lang/setup-dart@8a4b97ea2017cc079571daec46542f76189836b1 + - uses: dart-lang/setup-dart@b64355ae6ca0b5d484f0106a033dd1388965d06d with: sdk: ${{ matrix.sdk }} - id: install From 2700dfba2c7858bdec16e6d72d063f8b998facfd Mon Sep 17 00:00:00 2001 From: Futuristic Goo Date: Mon, 8 Jan 2024 04:24:14 +0530 Subject: [PATCH 243/260] Fix typo (dart-lang/async#262) --- pkgs/async/lib/src/cancelable_operation.dart | 2 +- pkgs/async/lib/src/result/result.dart | 4 ++-- pkgs/async/lib/src/subscription_stream.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index 0964d88a..bb59f60e 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -69,7 +69,7 @@ class CancelableOperation { /// /// Once any of [operations] completes, its result is forwarded to the /// new [CancelableOperation] and the rest are cancelled. If the - /// bew operation is cancelled, all the [operations] are cancelled as + /// new operation is cancelled, all the [operations] are cancelled as /// well. static CancelableOperation race( Iterable> operations) { diff --git a/pkgs/async/lib/src/result/result.dart b/pkgs/async/lib/src/result/result.dart index 2de3902f..124ccefa 100644 --- a/pkgs/async/lib/src/result/result.dart +++ b/pkgs/async/lib/src/result/result.dart @@ -17,8 +17,8 @@ import 'value.dart'; /// Capturing a result (either a returned value or a thrown error) means /// converting it into a [Result] - either a [ValueResult] or an [ErrorResult]. /// -/// This value can release itself by writing itself either to a [EventSink] or a -/// [Completer], or by becoming a [Future]. +/// This value can release itself by writing itself either to an [EventSink] or +/// a [Completer], or by becoming a [Future]. /// /// A [Future] represents a potential result, one that might not have been /// computed yet, and a [Result] is always a completed and available result. diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 6be88e30..5572adf8 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -14,7 +14,7 @@ import 'delegate/stream_subscription.dart'; /// then it is resumed and the events are passed on to the /// stream's new subscription. /// -/// This class assumes that is has control over the original subscription. +/// This class assumes that it has control over the original subscription. /// If other code is accessing the subscription, results may be unpredictable. class SubscriptionStream extends Stream { /// The subscription providing the events for this stream. From 87293d215067686610add56c7fe43bfaa25f9f66 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 11:20:05 +0000 Subject: [PATCH 244/260] Bump dart-lang/setup-dart from 1.6.0 to 1.6.2 (dart-lang/async#266) 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/async#43).
  • Fixed an issue where cached downloads could lead to unzip issues on self-hosted runners (dart-lang/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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index a3cc511e..04c07a67 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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: [2.19.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 42eddc9a448e7e211d22c5acf54af969cf60e732 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 15 Feb 2024 14:01:50 -0800 Subject: [PATCH 245/260] Update to latest lints, require Dart 3.2 (dart-lang/async#267) --- pkgs/async/.github/workflows/test-package.yml | 2 +- pkgs/async/CHANGELOG.md | 2 +- pkgs/async/analysis_options.yaml | 9 ++-- pkgs/async/lib/src/subscription_stream.dart | 2 +- pkgs/async/pubspec.yaml | 4 +- pkgs/async/test/async_cache_test.dart | 2 +- .../async/test/cancelable_operation_test.dart | 34 ++++++------ pkgs/async/test/chunked_stream_reader.dart | 4 +- pkgs/async/test/future_group_test.dart | 30 +++++------ pkgs/async/test/lazy_stream_test.dart | 6 +-- pkgs/async/test/null_stream_sink_test.dart | 12 ++--- pkgs/async/test/reject_errors_test.dart | 14 ++--- pkgs/async/test/restartable_timer_test.dart | 41 ++++++++------- .../test/result/result_captureAll_test.dart | 2 +- .../async/test/result/result_future_test.dart | 2 +- pkgs/async/test/result/result_test.dart | 22 ++++---- pkgs/async/test/sink_base_test.dart | 11 ++-- pkgs/async/test/stream_closer_test.dart | 2 +- pkgs/async/test/stream_completer_test.dart | 52 +++++++++---------- pkgs/async/test/stream_extensions_test.dart | 4 +- pkgs/async/test/stream_group_test.dart | 8 +-- pkgs/async/test/stream_queue_test.dart | 11 ++-- .../test/stream_sink_completer_test.dart | 10 ++-- .../test/stream_sink_transformer_test.dart | 6 +-- pkgs/async/test/stream_splitter_test.dart | 2 +- pkgs/async/test/stream_zip_test.dart | 8 +-- pkgs/async/test/stream_zip_zone_test.dart | 4 +- pkgs/async/test/subscription_stream_test.dart | 12 ++--- .../test/subscription_transformer_test.dart | 24 ++++----- pkgs/async/test/utils.dart | 10 ++-- 30 files changed, 178 insertions(+), 174 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 04c07a67..cf89e3c0 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.19.0, dev] + sdk: [3.2, dev] steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index ccda9d89..b2b6f25b 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.12.0-wip -- Require Dart 2.19 +- Require Dart 3.2 ## 2.11.0 diff --git a/pkgs/async/analysis_options.yaml b/pkgs/async/analysis_options.yaml index 260fdb0b..265204bd 100644 --- a/pkgs/async/analysis_options.yaml +++ b/pkgs/async/analysis_options.yaml @@ -1,4 +1,4 @@ -# https://dart.dev/guides/language/analysis-options +# https://dart.dev/tools/analysis#the-analysis-options-file include: package:dart_flutter_team_lints/analysis_options.yaml analyzer: @@ -7,16 +7,15 @@ analyzer: errors: only_throw_errors: ignore unawaited_futures: ignore + inference_failure_on_instance_creation: ignore + inference_failure_on_function_invocation: ignore + inference_failure_on_collection_literal: ignore linter: rules: - avoid_unused_constructor_parameters - - comment_references - literal_only_boolean_expressions - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - no_runtimeType_toString - package_api_docs - - prefer_relative_imports - - test_types_in_equals - - use_super_parameters diff --git a/pkgs/async/lib/src/subscription_stream.dart b/pkgs/async/lib/src/subscription_stream.dart index 5572adf8..9ce4942c 100644 --- a/pkgs/async/lib/src/subscription_stream.dart +++ b/pkgs/async/lib/src/subscription_stream.dart @@ -73,7 +73,7 @@ class _CancelOnErrorSubscriptionWrapper @override void onError(Function? handleError) { // Cancel when receiving an error. - super.onError((error, StackTrace stackTrace) { + super.onError((Object error, StackTrace stackTrace) { // Wait for the cancel to complete before sending the error event. super.cancel().whenComplete(() { if (handleError is ZoneBinaryCallback) { diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index dd2046c6..a7584221 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -4,14 +4,14 @@ description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async environment: - sdk: '>=2.19.0 <4.0.0' + sdk: ^3.2.0 dependencies: collection: ^1.15.0 meta: ^1.1.7 dev_dependencies: - dart_flutter_team_lints: ^1.0.0 + dart_flutter_team_lints: ^2.0.0 fake_async: ^1.2.0 stack_trace: ^1.10.0 test: ^1.16.0 diff --git a/pkgs/async/test/async_cache_test.dart b/pkgs/async/test/async_cache_test.dart index 77bda7c5..f7c8caa6 100644 --- a/pkgs/async/test/async_cache_test.dart +++ b/pkgs/async/test/async_cache_test.dart @@ -60,7 +60,7 @@ void main() { Future throwingCall() async => throw Exception(); await expectLater(cache.fetch(throwingCall), throwsA(isException)); // To let the timer invalidate the cache - await Future.delayed(Duration(milliseconds: 5)); + await Future.delayed(const Duration(milliseconds: 5)); Future call() async => 'Completed'; expect(await cache.fetch(call), 'Completed', reason: 'Cache invalidates'); diff --git a/pkgs/async/test/cancelable_operation_test.dart b/pkgs/async/test/cancelable_operation_test.dart index 6d415edd..3b096e42 100644 --- a/pkgs/async/test/cancelable_operation_test.dart +++ b/pkgs/async/test/cancelable_operation_test.dart @@ -105,7 +105,7 @@ void main() { }); test('is not complete until the result is available', () async { - var backingWork = Completer(); + var backingWork = Completer(); var operation = CancelableOperation.fromFuture(backingWork.future); expect(operation.isCompleted, isFalse); backingWork.complete(); @@ -132,17 +132,19 @@ void main() { test('successfully then with a future', () { completer.complete(1); - expect(() => completer.complete(Completer().future), throwsStateError); + expect(() => completer.complete(Completer().future), + throwsStateError); }); test('with a future then successfully', () { - completer.complete(Completer().future); + completer.complete(Completer().future); expect(() => completer.complete(1), throwsStateError); }); test('with a future twice', () { - completer.complete(Completer().future); - expect(() => completer.complete(Completer().future), throwsStateError); + completer.complete(Completer().future); + expect(() => completer.complete(Completer().future), + throwsStateError); }); }); @@ -185,7 +187,7 @@ void main() { group('when canceled', () { test('causes the future never to fire', () async { - var completer = CancelableCompleter(); + var completer = CancelableCompleter(); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); completer.operation.cancel(); @@ -242,7 +244,7 @@ void main() { 'does call onCancel if the completer has completed to an unfired ' 'Future', () { var completer = CancelableCompleter(onCancel: expectAsync0(() {})); - completer.complete(Completer().future); + completer.complete(Completer().future); expect(completer.operation.cancel(), completes); }); @@ -257,7 +259,7 @@ void main() { }); test('can be completed once after being canceled', () async { - var completer = CancelableCompleter(); + var completer = CancelableCompleter(); completer.operation.value.whenComplete(expectAsync0(() {}, count: 0)); await completer.operation.cancel(); completer.complete(1); @@ -265,7 +267,7 @@ void main() { }); test('fires valueOrCancellation with the given value', () { - var completer = CancelableCompleter(); + var completer = CancelableCompleter(); expect(completer.operation.valueOrCancellation(1), completion(equals(1))); completer.operation.cancel(); }); @@ -279,7 +281,7 @@ void main() { }); test('valueOrCancellation waits on the onCancel future', () async { - var innerCompleter = Completer(); + var innerCompleter = Completer(); var completer = CancelableCompleter(onCancel: () => innerCompleter.future); @@ -389,13 +391,13 @@ void main() { group('asStream()', () { test('emits a value and then closes', () { - var completer = CancelableCompleter(); + var completer = CancelableCompleter(); expect(completer.operation.asStream().toList(), completion(equals([1]))); completer.complete(1); }); test('emits an error and then closes', () { - var completer = CancelableCompleter(); + var completer = CancelableCompleter(); var queue = StreamQueue(completer.operation.asStream()); expect(queue.next, throwsA('error')); expect(queue.hasNext, completion(isFalse)); @@ -425,7 +427,7 @@ void main() { onError = expectAsync2((e, s) => 'Fake', count: 0, id: 'onError'); onCancel = expectAsync0(() => 'Fake', count: 0, id: 'onCancel'); propagateCancel = false; - originalCompleter = CancelableCompleter(); + originalCompleter = CancelableCompleter(); }); CancelableOperation runThen() { @@ -574,8 +576,8 @@ void main() { test('waits for chained cancellation', () async { var completer = CancelableCompleter(); var chainedOperation = completer.operation - .then((_) => Future.delayed(Duration(milliseconds: 1))) - .then((_) => Future.delayed(Duration(milliseconds: 1))); + .then((_) => Future.delayed(const Duration(milliseconds: 1))) + .then((_) => Future.delayed(const Duration(milliseconds: 1))); await completer.operation.cancel(); expect(completer.operation.isCanceled, true); @@ -655,7 +657,7 @@ void main() { onError = null; onCancel = null; propagateCancel = false; - originalCompleter = CancelableCompleter(); + originalCompleter = CancelableCompleter(); }); CancelableOperation runThenOperation() { diff --git a/pkgs/async/test/chunked_stream_reader.dart b/pkgs/async/test/chunked_stream_reader.dart index b4dd4f0a..2fc1e8b7 100644 --- a/pkgs/async/test/chunked_stream_reader.dart +++ b/pkgs/async/test/chunked_stream_reader.dart @@ -342,7 +342,7 @@ void main() { final r = ChunkedStreamReader(() async* { yield [1, 2, 3]; // This will hang forever, so we will call cancel() - await Completer().future; + await Completer().future; yield [4]; // this should never be reachable fail('unreachable!'); }()); @@ -362,7 +362,7 @@ void main() { final r = ChunkedStreamReader(() async* { yield [1, 2, 3]; // This will hang forever, so we will call cancel() - await Completer().future; + await Completer().future; yield [4]; // this should never be reachable fail('unreachable!'); }()); diff --git a/pkgs/async/test/future_group_test.dart b/pkgs/async/test/future_group_test.dart index c2b1f3f0..9729c066 100644 --- a/pkgs/async/test/future_group_test.dart +++ b/pkgs/async/test/future_group_test.dart @@ -67,9 +67,9 @@ void main() { }); test('completes once all contained futures complete', () async { - var completer1 = Completer(); - var completer2 = Completer(); - var completer3 = Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -93,9 +93,9 @@ void main() { }); test('completes to the values of the futures in order of addition', () { - var completer1 = Completer(); - var completer2 = Completer(); - var completer3 = Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -112,9 +112,9 @@ void main() { test("completes to the first error to be emitted, even if it's not closed", () { - var completer1 = Completer(); - var completer2 = Completer(); - var completer3 = Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -130,9 +130,9 @@ void main() { var idle = false; futureGroup.onIdle.listen((_) => idle = true); - var completer1 = Completer(); - var completer2 = Completer(); - var completer3 = Completer(); + var completer1 = Completer(); + var completer2 = Completer(); + var completer3 = Completer(); futureGroup.add(completer1.future); futureGroup.add(completer2.future); @@ -162,7 +162,7 @@ void main() { var idle = false; futureGroup.onIdle.listen((_) => idle = true); - var completer = Completer(); + var completer = Completer(); futureGroup.add(completer.future); completer.complete(); @@ -171,7 +171,7 @@ void main() { expect(futureGroup.isIdle, isTrue); idle = false; - completer = Completer(); + completer = Completer(); futureGroup.add(completer.future); await flushMicrotasks(); @@ -206,7 +206,7 @@ void main() { futureFired = true; })); - var completer = Completer(); + var completer = Completer(); futureGroup.add(completer.future); futureGroup.close(); diff --git a/pkgs/async/test/lazy_stream_test.dart b/pkgs/async/test/lazy_stream_test.dart index cde1928f..9785b2e1 100644 --- a/pkgs/async/test/lazy_stream_test.dart +++ b/pkgs/async/test/lazy_stream_test.dart @@ -14,7 +14,7 @@ void main() { var callbackCalled = false; var stream = LazyStream(expectAsync0(() { callbackCalled = true; - return Stream.empty(); + return const Stream.empty(); })); await flushMicrotasks(); @@ -28,7 +28,7 @@ void main() { var callbackCalled = false; var stream = LazyStream(expectAsync0(() { callbackCalled = true; - return Stream.empty(); + return const Stream.empty(); })); await flushMicrotasks(); @@ -95,7 +95,7 @@ void main() { late LazyStream stream; stream = LazyStream(expectAsync0(() { expect(() => stream.listen(null), throwsStateError); - return Stream.empty(); + return const Stream.empty(); })); stream.listen(null); }); diff --git a/pkgs/async/test/null_stream_sink_test.dart b/pkgs/async/test/null_stream_sink_test.dart index 649cbad1..16d69866 100644 --- a/pkgs/async/test/null_stream_sink_test.dart +++ b/pkgs/async/test/null_stream_sink_test.dart @@ -17,7 +17,7 @@ void main() { }); test('a custom future may be passed to done', () async { - var completer = Completer(); + var completer = Completer(); var sink = NullStreamSink(done: completer.future); var doneFired = false; @@ -51,7 +51,7 @@ void main() { expect(() => sink.add(1), throwsStateError); expect(() => sink.addError('oh no'), throwsStateError); - expect(() => sink.addStream(Stream.empty()), throwsStateError); + expect(() => sink.addStream(const Stream.empty()), throwsStateError); }); group('addStream', () { @@ -68,7 +68,7 @@ void main() { }); test('returns the cancel future', () async { - var completer = Completer(); + var completer = Completer(); var sink = NullStreamSink(); var controller = StreamController(onCancel: () => completer.future); @@ -93,15 +93,15 @@ void main() { test('causes events to throw StateErrors until the future completes', () async { var sink = NullStreamSink(); - var future = sink.addStream(Stream.empty()); + var future = sink.addStream(const Stream.empty()); expect(() => sink.add(1), throwsStateError); expect(() => sink.addError('oh no'), throwsStateError); - expect(() => sink.addStream(Stream.empty()), throwsStateError); + expect(() => sink.addStream(const Stream.empty()), throwsStateError); await future; sink.add(1); sink.addError('oh no'); - expect(sink.addStream(Stream.empty()), completes); + expect(sink.addStream(const Stream.empty()), completes); }); }); }); diff --git a/pkgs/async/test/reject_errors_test.dart b/pkgs/async/test/reject_errors_test.dart index 5b8b3e77..27e3c255 100644 --- a/pkgs/async/test/reject_errors_test.dart +++ b/pkgs/async/test/reject_errors_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; void main() { late StreamController controller; setUp(() { - controller = StreamController(); + controller = StreamController(); }); test('passes through data events', () { @@ -89,7 +89,7 @@ void main() { group('when the inner sink\'s done future completes', () { test('done completes', () async { - var completer = Completer(); + var completer = Completer(); var transformed = NullStreamSink(done: completer.future).rejectErrors(); var doneCompleted = false; @@ -103,7 +103,7 @@ void main() { }); test('an outstanding addStream() completes', () async { - var completer = Completer(); + var completer = Completer(); var transformed = NullStreamSink(done: completer.future).rejectErrors(); var addStreamCompleted = false; @@ -119,7 +119,7 @@ void main() { }); test('an outstanding addStream()\'s subscription is cancelled', () async { - var completer = Completer(); + var completer = Completer(); var transformed = NullStreamSink(done: completer.future).rejectErrors(); var addStreamCancelled = false; @@ -134,7 +134,7 @@ void main() { }); test('forwards an outstanding addStream()\'s cancellation error', () async { - var completer = Completer(); + var completer = Completer(); var transformed = NullStreamSink(done: completer.future).rejectErrors(); expect( @@ -171,7 +171,7 @@ void main() { test('throws on addStream()', () { var sink = controller.sink.rejectErrors()..close(); - expect(() => sink.addStream(Stream.empty()), throwsStateError); + expect(() => sink.addStream(const Stream.empty()), throwsStateError); }); test('allows close()', () { @@ -196,7 +196,7 @@ void main() { test('throws on addStream()', () { var sink = controller.sink.rejectErrors() ..addStream(StreamController().stream); - expect(() => sink.addStream(Stream.empty()), throwsStateError); + expect(() => sink.addStream(const Stream.empty()), throwsStateError); }); test('throws on close()', () { diff --git a/pkgs/async/test/restartable_timer_test.dart b/pkgs/async/test/restartable_timer_test.dart index 41f52ab9..4aab2871 100644 --- a/pkgs/async/test/restartable_timer_test.dart +++ b/pkgs/async/test/restartable_timer_test.dart @@ -10,14 +10,14 @@ void main() { test('runs the callback once the duration has elapsed', () { FakeAsync().run((async) { var fired = false; - RestartableTimer(Duration(seconds: 5), () { + RestartableTimer(const Duration(seconds: 5), () { fired = true; }); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); - async.elapse(Duration(seconds: 1)); + async.elapse(const Duration(seconds: 1)); expect(fired, isTrue); }); }); @@ -25,15 +25,15 @@ void main() { test("doesn't run the callback if the timer is canceled", () { FakeAsync().run((async) { var fired = false; - var timer = RestartableTimer(Duration(seconds: 5), () { + var timer = RestartableTimer(const Duration(seconds: 5), () { fired = true; }); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); timer.cancel(); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); }); }); @@ -41,18 +41,18 @@ void main() { test('resets the duration if the timer is reset before it fires', () { FakeAsync().run((async) { var fired = false; - var timer = RestartableTimer(Duration(seconds: 5), () { + var timer = RestartableTimer(const Duration(seconds: 5), () { fired = true; }); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); timer.reset(); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); - async.elapse(Duration(seconds: 1)); + async.elapse(const Duration(seconds: 1)); expect(fired, isTrue); }); }); @@ -60,19 +60,19 @@ void main() { test('re-runs the callback if the timer is reset after firing', () { FakeAsync().run((async) { var fired = 0; - var timer = RestartableTimer(Duration(seconds: 5), () { + var timer = RestartableTimer(const Duration(seconds: 5), () { fired++; }); - async.elapse(Duration(seconds: 5)); + async.elapse(const Duration(seconds: 5)); expect(fired, equals(1)); timer.reset(); - async.elapse(Duration(seconds: 5)); + async.elapse(const Duration(seconds: 5)); expect(fired, equals(2)); timer.reset(); - async.elapse(Duration(seconds: 5)); + async.elapse(const Duration(seconds: 5)); expect(fired, equals(3)); }); }); @@ -80,27 +80,28 @@ void main() { test('runs the callback if the timer is reset after being canceled', () { FakeAsync().run((async) { var fired = false; - var timer = RestartableTimer(Duration(seconds: 5), () { + var timer = RestartableTimer(const Duration(seconds: 5), () { fired = true; }); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); timer.cancel(); - async.elapse(Duration(seconds: 4)); + async.elapse(const Duration(seconds: 4)); expect(fired, isFalse); timer.reset(); - async.elapse(Duration(seconds: 5)); + async.elapse(const Duration(seconds: 5)); expect(fired, isTrue); }); }); test("only runs the callback once if the timer isn't reset", () { FakeAsync().run((async) { - RestartableTimer(Duration(seconds: 5), expectAsync0(() {}, count: 1)); - async.elapse(Duration(seconds: 10)); + RestartableTimer( + const Duration(seconds: 5), expectAsync0(() {}, count: 1)); + async.elapse(const Duration(seconds: 10)); }); }); } diff --git a/pkgs/async/test/result/result_captureAll_test.dart b/pkgs/async/test/result/result_captureAll_test.dart index 25c57759..e85999e9 100644 --- a/pkgs/async/test/result/result_captureAll_test.dart +++ b/pkgs/async/test/result/result_captureAll_test.dart @@ -147,7 +147,7 @@ void main() { expect(all, completion(expected)); - var completeFunctions = List.generate(n, (i) { + var completeFunctions = List.generate(n, (i) { var c = cs[i]; return () => throws(i) ? c.completeError('$i', someStack) : c.complete(i); diff --git a/pkgs/async/test/result/result_future_test.dart b/pkgs/async/test/result/result_future_test.dart index 71710054..de218840 100644 --- a/pkgs/async/test/result/result_future_test.dart +++ b/pkgs/async/test/result/result_future_test.dart @@ -12,7 +12,7 @@ void main() { late Completer completer; late ResultFuture future; setUp(() { - completer = Completer(); + completer = Completer(); future = ResultFuture(completer.future); }); diff --git a/pkgs/async/test/result/result_test.dart b/pkgs/async/test/result/result_test.dart index 568a8910..13a5d536 100644 --- a/pkgs/async/test/result/result_test.dart +++ b/pkgs/async/test/result/result_test.dart @@ -61,7 +61,7 @@ void main() { var c = Completer(); c.future.then(expectAsync1((int v) { expect(v, equals(42)); - }), onError: (e, s) { + }), onError: (Object? e, s) { fail('Unexpected error'); }); result.complete(c); @@ -100,7 +100,7 @@ void main() { Result result = ValueResult(42); result.asFuture.then(expectAsync1((int v) { expect(v, equals(42)); - }), onError: (e, s) { + }), onError: (Object? e, s) { fail('Unexpected error'); }); }); @@ -122,7 +122,7 @@ void main() { expect(result.isError, isFalse); var value = result.asValue!; expect(value.value, equals(42)); - }), onError: (e, s) { + }), onError: (Object? e, s) { fail('Unexpected error: $e'); }); }); @@ -135,7 +135,7 @@ void main() { var error = result.asError!; expect(error.error, equals('BAD')); expect(error.stackTrace, same(stack)); - }), onError: (e, s) { + }), onError: (Object? e, s) { fail('Unexpected error: $e'); }); }); @@ -144,7 +144,7 @@ void main() { var future = Future>.value(Result.value(42)); Result.release(future).then(expectAsync1((v) { expect(v, equals(42)); - }), onError: (e, s) { + }), onError: (Object? e, s) { fail('Unexpected error: $e'); }); }); @@ -207,7 +207,7 @@ void main() { expect(v, equals(expected.asValue!.value)); } - void errorListener(error, StackTrace stackTrace) { + void errorListener(Object error, StackTrace stackTrace) { expect(expectedList.isEmpty, isFalse); Result expected = expectedList.removeFirst(); expect(expected.isError, isTrue); @@ -263,7 +263,7 @@ void main() { test('handle unary', () { var result = ErrorResult('error', stack); var called = false; - result.handle((error) { + result.handle((Object? error) { called = true; expect(error, 'error'); }); @@ -273,7 +273,7 @@ void main() { test('handle binary', () { var result = ErrorResult('error', stack); var called = false; - result.handle((error, stackTrace) { + result.handle((Object? error, Object? stackTrace) { called = true; expect(error, 'error'); expect(stackTrace, same(stack)); @@ -284,7 +284,7 @@ void main() { test('handle unary and binary', () { var result = ErrorResult('error', stack); var called = false; - result.handle((error, [stackTrace]) { + result.handle((Object? error, [Object? stackTrace]) { called = true; expect(error, 'error'); expect(stackTrace, same(stack)); @@ -344,11 +344,11 @@ class TestSink implements EventSink { onDone(); } - static void _nullData(value) { + static void _nullData(dynamic value) { fail('Unexpected sink add: $value'); } - static void _nullError(e, StackTrace s) { + static void _nullError(dynamic e, StackTrace s) { fail('Unexpected sink addError: $e'); } diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart index 95f51c90..9cf8c10d 100644 --- a/pkgs/async/test/sink_base_test.dart +++ b/pkgs/async/test/sink_base_test.dart @@ -80,7 +80,7 @@ void main() { }); test('all invocations of close() return the same future', () async { - var completer = Completer(); + var completer = Completer(); var sink = _StreamSink(onClose: expectAsync0(() => completer.future)); var close1Completed = false; @@ -106,7 +106,7 @@ void main() { test('done returns a future that completes once close() completes', () async { - var completer = Completer(); + var completer = Completer(); var sink = _StreamSink(onClose: expectAsync0(() => completer.future)); var doneCompleted = false; @@ -297,7 +297,7 @@ void main() { group('flush()', () { test('returns a future that completes when onFlush() is done', () async { - var completer = Completer(); + var completer = Completer(); var sink = _IOSink(onFlush: expectAsync0(() => completer.future)); var flushDone = false; @@ -324,11 +324,12 @@ void main() { }); test('locks the sink as though a stream was being added', () { - var sink = _IOSink(onFlush: expectAsync0(() => Completer().future)); + var sink = + _IOSink(onFlush: expectAsync0(() => Completer().future)); sink.flush(); expect(() => sink.add([0]), throwsStateError); expect(() => sink.addError('oh no'), throwsStateError); - expect(() => sink.addStream(Stream.empty()), throwsStateError); + expect(() => sink.addStream(const Stream.empty()), throwsStateError); expect(() => sink.flush(), throwsStateError); expect(() => sink.close(), throwsStateError); }); diff --git a/pkgs/async/test/stream_closer_test.dart b/pkgs/async/test/stream_closer_test.dart index 28ab9703..a2bad1a9 100644 --- a/pkgs/async/test/stream_closer_test.dart +++ b/pkgs/async/test/stream_closer_test.dart @@ -27,7 +27,7 @@ void main() { }); test('transforms a broadcast stream into a broadcast stream', () { - expect(Stream.empty().transform(closer).isBroadcast, isTrue); + expect(const Stream.empty().transform(closer).isBroadcast, isTrue); }); test("doesn't eagerly listen", () { diff --git a/pkgs/async/test/stream_completer_test.dart b/pkgs/async/test/stream_completer_test.dart index eaeb7192..f58162e4 100644 --- a/pkgs/async/test/stream_completer_test.dart +++ b/pkgs/async/test/stream_completer_test.dart @@ -11,13 +11,13 @@ import 'utils.dart'; void main() { test('a stream is linked before listening', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.setSourceStream(createStream()); expect(completer.stream.toList(), completion([1, 2, 3, 4])); }); test('listened to before a stream is linked', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); var done = completer.stream.toList(); await flushMicrotasks(); completer.setSourceStream(createStream()); @@ -25,7 +25,7 @@ void main() { }); test("cancel before linking a stream doesn't listen on stream", () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); var subscription = completer.stream.listen(null); subscription.pause(); // Should be ignored. subscription.cancel(); @@ -33,12 +33,12 @@ void main() { }); test('listen and pause before linking stream', () async { - var controller = StreamCompleter(); + var controller = StreamCompleter(); var events = []; var subscription = controller.stream.listen(events.add); var done = subscription.asFuture(); subscription.pause(); - var sourceController = StreamController(); + var sourceController = StreamController(); sourceController ..add(1) ..add(2) @@ -60,7 +60,7 @@ void main() { }); test('pause more than once', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); var events = []; var subscription = completer.stream.listen(events.add); var done = subscription.asFuture(); @@ -110,17 +110,17 @@ void main() { }); test('complete with setEmpty before listening', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.setEmpty(); - var done = Completer(); + var done = Completer(); completer.stream.listen(unreachable('data'), onError: unreachable('error'), onDone: done.complete); await done.future; }); test('complete with setEmpty after listening', () async { - var completer = StreamCompleter(); - var done = Completer(); + var completer = StreamCompleter(); + var done = Completer(); completer.stream.listen(unreachable('data'), onError: unreachable('error'), onDone: done.complete); completer.setEmpty(); @@ -128,7 +128,7 @@ void main() { }); test("source stream isn't listened to until completer stream is", () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); late StreamController controller; controller = StreamController(onListen: () { scheduleMicrotask(controller.close); @@ -208,7 +208,7 @@ void main() { }); test('linking a stream after setSourceStream before listen', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.setSourceStream(createStream()); expect(() => completer.setSourceStream(createStream()), throwsStateError); expect(() => completer.setEmpty(), throwsStateError); @@ -219,7 +219,7 @@ void main() { }); test('linking a stream after setSourceStream after listen', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); var list = completer.stream.toList(); completer.setSourceStream(createStream()); expect(() => completer.setSourceStream(createStream()), throwsStateError); @@ -231,7 +231,7 @@ void main() { }); test('linking a stream after setEmpty before listen', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.setEmpty(); expect(() => completer.setSourceStream(createStream()), throwsStateError); expect(() => completer.setEmpty(), throwsStateError); @@ -242,7 +242,7 @@ void main() { }); test('linking a stream after setEmpty() after listen', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); var list = completer.stream.toList(); completer.setEmpty(); expect(() => completer.setSourceStream(createStream()), throwsStateError); @@ -254,7 +254,7 @@ void main() { }); test('listening more than once after setting stream', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.setSourceStream(createStream()); var list = completer.stream.toList(); expect(() => completer.stream.toList(), throwsStateError); @@ -263,7 +263,7 @@ void main() { }); test('listening more than once before setting stream', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.stream.toList(); expect(() => completer.stream.toList(), throwsStateError); }); @@ -274,7 +274,7 @@ void main() { var subscription = completer.stream.listen(null); Object lastEvent = 0; subscription.onData((value) => lastEvent = value); - subscription.onError((value) => lastEvent = '$value'); + subscription.onError((Object value) => lastEvent = '$value'); subscription.onDone(() => lastEvent = -1); completer.setSourceStream(controller.stream); await flushMicrotasks(); @@ -285,7 +285,7 @@ void main() { await flushMicrotasks(); expect(lastEvent, '2'); subscription.onData((value) => lastEvent = -value); - subscription.onError((value) => lastEvent = '${-(value as int)}'); + subscription.onError((Object value) => lastEvent = '${-(value as int)}'); controller.add(1); await flushMicrotasks(); expect(lastEvent, -1); @@ -298,8 +298,8 @@ void main() { }); test('pause w/ resume future accross setting stream', () async { - var completer = StreamCompleter(); - var resume = Completer(); + var completer = StreamCompleter(); + var resume = Completer(); var subscription = completer.stream.listen(unreachable('data')); subscription.pause(resume.future); await flushMicrotasks(); @@ -313,8 +313,8 @@ void main() { }); test('asFuture with error accross setting stream', () async { - var completer = StreamCompleter(); - var controller = StreamController(); + var completer = StreamCompleter(); + var controller = StreamController(); var subscription = completer.stream.listen(unreachable('data'), cancelOnError: false); var done = subscription.asFuture(); @@ -323,7 +323,7 @@ void main() { await flushMicrotasks(); expect(controller.hasListener, isTrue); controller.addError(42); - await done.then(unreachable('data'), onError: (error) { + await done.then(unreachable('data'), onError: (Object error) { expect(error, 42); }); expect(controller.hasListener, isFalse); @@ -331,7 +331,7 @@ void main() { group('setError()', () { test('produces a stream that emits a single error', () { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.stream.listen(unreachable('data'), onError: expectAsync2((error, stackTrace) { expect(error, equals('oh no')); @@ -342,7 +342,7 @@ void main() { test('produces a stream that emits a single error on a later listen', () async { - var completer = StreamCompleter(); + var completer = StreamCompleter(); completer.setError('oh no'); await flushMicrotasks(); diff --git a/pkgs/async/test/stream_extensions_test.dart b/pkgs/async/test/stream_extensions_test.dart index c9229232..b43dedc1 100644 --- a/pkgs/async/test/stream_extensions_test.dart +++ b/pkgs/async/test/stream_extensions_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; void main() { group('.slices', () { test('empty', () { - expect(Stream.empty().slices(1).toList(), completion(equals([]))); + expect(const Stream.empty().slices(1).toList(), completion(equals([]))); }); test('with the same length as the iterable', () { @@ -67,7 +67,7 @@ void main() { }); test('returns null for an empty stream', () { - expect(Stream.empty().firstOrNull, completion(isNull)); + expect(const Stream.empty().firstOrNull, completion(isNull)); }); test('cancels the subscription after an event', () async { diff --git a/pkgs/async/test/stream_group_test.dart b/pkgs/async/test/stream_group_test.dart index 5c684f30..3700120e 100644 --- a/pkgs/async/test/stream_group_test.dart +++ b/pkgs/async/test/stream_group_test.dart @@ -162,7 +162,7 @@ void main() { test('forwards a cancel future', () async { var subscription = streamGroup.stream.listen(null); - var completer = Completer(); + var completer = Completer(); var controller = StreamController(onCancel: () => completer.future); streamGroup.add(controller.stream); @@ -231,7 +231,7 @@ void main() { }); test('forwards a cancel future', () async { - var completer = Completer(); + var completer = Completer(); var controller = StreamController(onCancel: () => completer.future); @@ -680,7 +680,7 @@ void regardlessOfType(StreamGroup Function() newStreamGroup) { }); test('forwards cancel futures', () async { - var completer = Completer(); + var completer = Completer(); var controller = StreamController(onCancel: () => completer.future); @@ -923,4 +923,4 @@ void regardlessOfType(StreamGroup Function() newStreamGroup) { } /// Wait for all microtasks to complete. -Future flushMicrotasks() => Future.delayed(Duration.zero); +Future flushMicrotasks() => Future.delayed(Duration.zero); diff --git a/pkgs/async/test/stream_queue_test.dart b/pkgs/async/test/stream_queue_test.dart index 7c575c09..cd4433ae 100644 --- a/pkgs/async/test/stream_queue_test.dart +++ b/pkgs/async/test/stream_queue_test.dart @@ -250,7 +250,8 @@ void main() { var skip4 = events.skip(1); var index = 0; // Check that futures complete in order. - Func1Required sequence(expectedValue, sequenceIndex) => (value) { + Func1Required sequence(int expectedValue, int sequenceIndex) => + (value) { expect(value, expectedValue); expect(index, sequenceIndex); index++; @@ -390,7 +391,7 @@ void main() { }); test('forwards to underlying stream', () async { - var cancel = Completer(); + var cancel = Completer(); var controller = StreamController(onCancel: () => cancel.future); var events = StreamQueue(controller.stream); expect(controller.hasListener, isFalse); @@ -872,7 +873,7 @@ void main() { // Regression test. test('pending child rest requests emit no more events', () async { - var controller = StreamController(); + var controller = StreamController(); var events = StreamQueue(controller.stream); var transaction = events.startTransaction(); var queue = transaction.newQueue(); @@ -906,7 +907,7 @@ void main() { }); test('before the transaction emits any events, does nothing', () async { - var controller = StreamController(); + var controller = StreamController(); var events = StreamQueue(controller.stream); // Queue a request before the transaction, but don't let it complete @@ -1002,7 +1003,7 @@ void main() { }); test('before the transaction emits any events, does nothing', () async { - var controller = StreamController(); + var controller = StreamController(); var events = StreamQueue(controller.stream); // Queue a request before the transaction, but don't let it complete diff --git a/pkgs/async/test/stream_sink_completer_test.dart b/pkgs/async/test/stream_sink_completer_test.dart index 11592bb7..3a6f25bc 100644 --- a/pkgs/async/test/stream_sink_completer_test.dart +++ b/pkgs/async/test/stream_sink_completer_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { late StreamSinkCompleter completer; setUp(() { - completer = StreamSinkCompleter(); + completer = StreamSinkCompleter(); }); group('when a stream is linked before events are added', () { @@ -46,7 +46,7 @@ void main() { var sink = TestSink(); completer.setDestinationSink(sink); - var controller = StreamController(); + var controller = StreamController(); completer.sink.addStream(controller.stream); controller.add(1); @@ -74,7 +74,7 @@ void main() { }); test('the future from the inner close() is returned', () async { - var closeCompleter = Completer(); + var closeCompleter = Completer(); var sink = TestSink(onDone: () => closeCompleter.future); completer.setDestinationSink(sink); @@ -152,7 +152,7 @@ void main() { }); test('addStream is forwarded', () async { - var controller = StreamController(); + var controller = StreamController(); completer.sink.addStream(controller.stream); controller.add(1); @@ -191,7 +191,7 @@ void main() { })); await flushMicrotasks(); - var closeCompleter = Completer(); + var closeCompleter = Completer(); var sink = TestSink(onDone: () => closeCompleter.future); completer.setDestinationSink(sink); await flushMicrotasks(); diff --git a/pkgs/async/test/stream_sink_transformer_test.dart b/pkgs/async/test/stream_sink_transformer_test.dart index e5f6baa1..caea3495 100644 --- a/pkgs/async/test/stream_sink_transformer_test.dart +++ b/pkgs/async/test/stream_sink_transformer_test.dart @@ -12,7 +12,7 @@ import 'utils.dart'; void main() { late StreamController controller; setUp(() { - controller = StreamController(); + controller = StreamController(); }); group('fromStreamTransformer', () { @@ -43,7 +43,7 @@ void main() { var results = []; controller.stream.listen(expectAsync1((_) {}, count: 0), - onError: (error, stackTrace) { + onError: (Object error, stackTrace) { results.add(error); }, onDone: expectAsync0(() { expect(results, equals([2, 4, 6])); @@ -142,7 +142,7 @@ void main() { var results = []; controller.stream.listen(expectAsync1((_) {}, count: 0), - onError: (error, stackTrace) { + onError: (Object error, stackTrace) { results.add(error); }, onDone: expectAsync0(() { expect(results, equals([2, 4, 6])); diff --git a/pkgs/async/test/stream_splitter_test.dart b/pkgs/async/test/stream_splitter_test.dart index 43fe3922..27921db1 100644 --- a/pkgs/async/test/stream_splitter_test.dart +++ b/pkgs/async/test/stream_splitter_test.dart @@ -287,4 +287,4 @@ void main() { } /// Wait for all microtasks to complete. -Future flushMicrotasks() => Future.delayed(Duration.zero); +Future flushMicrotasks() => Future.delayed(Duration.zero); diff --git a/pkgs/async/test/stream_zip_test.dart b/pkgs/async/test/stream_zip_test.dart index 71ce2c46..147d419c 100644 --- a/pkgs/async/test/stream_zip_test.dart +++ b/pkgs/async/test/stream_zip_test.dart @@ -225,7 +225,7 @@ void main() { }); test('Error after first end', () { - var controller = StreamController(); + var controller = StreamController(); controller ..add(7) ..add(8) @@ -289,7 +289,7 @@ void main() { }).then((hasMore) { expect(hasMore, isTrue); expect(it.current, equals([5, 6])); - Future.delayed(ms25).then((_) { + Future.delayed(ms25).then((_) { c2.add(8); }); return it.moveNext(); @@ -323,10 +323,10 @@ void main() { sub = sz.listen(expectAsync1((v) { expect(v, equals([ctr * 2, ctr * 2 + 1])); if (ctr == 1) { - sub.pause(Future.delayed(const Duration(milliseconds: 25))); + sub.pause(Future.delayed(const Duration(milliseconds: 25))); } else if (ctr == 2) { sub.pause(); - Future.delayed(const Duration(milliseconds: 25)).then((_) { + Future.delayed(const Duration(milliseconds: 25)).then((_) { sub.resume(); }); } diff --git a/pkgs/async/test/stream_zip_zone_test.dart b/pkgs/async/test/stream_zip_zone_test.dart index 50af6b62..a18c7761 100644 --- a/pkgs/async/test/stream_zip_zone_test.dart +++ b/pkgs/async/test/stream_zip_zone_test.dart @@ -11,11 +11,11 @@ import 'package:test/test.dart'; void main() { StreamController controller; - controller = StreamController(); + controller = StreamController(); testStream('singlesub-async', controller, controller.stream); controller = StreamController.broadcast(); testStream('broadcast-async', controller, controller.stream); - controller = StreamController(); + controller = StreamController(); testStream( 'asbroadcast-async', controller, controller.stream.asBroadcastStream()); diff --git a/pkgs/async/test/subscription_stream_test.dart b/pkgs/async/test/subscription_stream_test.dart index ea626b12..0d245f38 100644 --- a/pkgs/async/test/subscription_stream_test.dart +++ b/pkgs/async/test/subscription_stream_test.dart @@ -76,7 +76,7 @@ void main() { late Future onCancel; // Completes if source stream is canceled before done. setUp(() { - var cancelCompleter = Completer(); + var cancelCompleter = Completer(); var source = createErrorStream(cancelCompleter); onCancel = cancelCompleter.future; var sourceSubscription = @@ -85,7 +85,7 @@ void main() { }); test('- subscriptionStream: no', () async { - var done = Completer(); + var done = Completer(); var events = []; subscriptionStream.listen(events.add, onError: events.add, onDone: done.complete, cancelOnError: false); @@ -97,7 +97,7 @@ void main() { done.future.then((_) { isDone = true; }); - await Future.delayed(const Duration(milliseconds: 5)); + await Future.delayed(const Duration(milliseconds: 5)); expect(isDone, false); } else { expected.add(4); @@ -107,10 +107,10 @@ void main() { }); test('- subscriptionStream: yes', () async { - var completer = Completer(); - var events = []; + var completer = Completer(); + var events = []; subscriptionStream.listen(events.add, - onError: (value) { + onError: (Object? value) { events.add(value); completer.complete(); }, diff --git a/pkgs/async/test/subscription_transformer_test.dart b/pkgs/async/test/subscription_transformer_test.dart index a95c7c46..53610e19 100644 --- a/pkgs/async/test/subscription_transformer_test.dart +++ b/pkgs/async/test/subscription_transformer_test.dart @@ -41,7 +41,7 @@ void main() { }); test('forwards pausing and resuming', () async { - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer()) .listen(expectAsync1((_) {}, count: 0)); @@ -64,12 +64,12 @@ void main() { }); test('forwards pausing with a resume future', () async { - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer()) .listen(expectAsync1((_) {}, count: 0)); - var completer = Completer(); + var completer = Completer(); subscription.pause(completer.future); await flushMicrotasks(); expect(controller.isPaused, isTrue); @@ -105,8 +105,8 @@ void main() { }); test('invokes the callback once and caches its result', () async { - var completer = Completer(); - var controller = StreamController(); + var completer = Completer(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handleCancel: expectAsync1((inner) => completer.future))) @@ -136,7 +136,7 @@ void main() { group('with a pause callback', () { test('invokes the callback when pause is called', () async { var pauseCount = 0; - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handlePause: expectAsync1((inner) { @@ -168,7 +168,7 @@ void main() { test("doesn't invoke the callback when the subscription has been canceled", () async { - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handlePause: expectAsync1((_) {}, count: 0))) @@ -184,7 +184,7 @@ void main() { group('with a resume callback', () { test('invokes the callback when resume is called', () async { var resumeCount = 0; - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handleResume: expectAsync1((inner) { @@ -216,14 +216,14 @@ void main() { test('invokes the callback when a resume future completes', () async { var resumed = false; - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream.transform( subscriptionTransformer(handleResume: expectAsync1((inner) { resumed = true; inner.resume(); }))).listen(expectAsync1((_) {}, count: 0)); - var completer = Completer(); + var completer = Completer(); subscription.pause(completer.future); await flushMicrotasks(); expect(resumed, isFalse); @@ -235,7 +235,7 @@ void main() { test("doesn't invoke the callback when the subscription has been canceled", () async { - var controller = StreamController(); + var controller = StreamController(); var subscription = controller.stream .transform(subscriptionTransformer( handlePause: expectAsync1((_) {}, count: 0))) @@ -251,7 +251,7 @@ void main() { group('when the outer subscription is canceled but the inner is not', () { late StreamSubscription subscription; setUp(() { - var controller = StreamController(); + var controller = StreamController(); subscription = controller.stream .transform( subscriptionTransformer(handleCancel: (_) => Future.value())) diff --git a/pkgs/async/test/utils.dart b/pkgs/async/test/utils.dart index fd87a4ef..0a6b339a 100644 --- a/pkgs/async/test/utils.dart +++ b/pkgs/async/test/utils.dart @@ -11,7 +11,7 @@ import 'package:async/async.dart'; import 'package:test/test.dart'; /// A zero-millisecond timer should wait until after all microtasks. -Future flushMicrotasks() => Future.delayed(Duration.zero); +Future flushMicrotasks() => Future.delayed(Duration.zero); typedef OptionalArgAction = void Function([dynamic a, dynamic b]); @@ -40,10 +40,10 @@ Matcher throwsZoned(Matcher matcher) => predicate((void Function() callback) { /// A matcher that runs a callback in its own zone and asserts that that zone /// emits a [TypeError]. -final throwsZonedTypeError = throwsZoned(TypeMatcher()); +final throwsZonedTypeError = throwsZoned(isA()); /// A matcher that matches a callback or future that throws a [TypeError]. -final throwsTypeError = throwsA(TypeMatcher()); +final throwsTypeError = throwsA(isA()); /// A badly behaved stream which throws if it's ever listened to. /// @@ -62,7 +62,7 @@ class UnusableStream extends Stream { /// The [completer] field allows the user to control the future returned by /// [done] and [close]. class CompleterStreamSink implements StreamSink { - final completer = Completer(); + final completer = Completer(); @override Future get done => completer.future; @@ -90,7 +90,7 @@ class TestSink implements StreamSink { @override Future get done => _doneCompleter.future; - final _doneCompleter = Completer(); + final _doneCompleter = Completer(); final void Function() _onDone; From 6ca5fba23f6111ec25a12b56bc253088ee07a900 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 7 Mar 2024 11:04:12 -0800 Subject: [PATCH 246/260] Test on wasm (dev) and JS (dart-lang/async#269) --- pkgs/async/.github/workflows/test-package.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index cf89e3c0..c1132b15 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/async/.github/workflows/test-package.yml @@ -59,3 +59,8 @@ jobs: - name: Run VM tests run: dart test --platform vm if: always() && steps.install.outcome == 'success' + - run: dart test --platform chrome --compiler dart2js + if: always() && steps.install.outcome == 'success' + - run: dart test --platform chrome --compiler dart2wasm + # TODO: drop `dev` filter when dart2wasm is working on stable + if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' From 9b89930e7fef0774614960611b27775e854fc512 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:42:48 +0000 Subject: [PATCH 247/260] Bump actions/checkout from 4.1.1 to 4.1.2 (dart-lang/async#270) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index c1132b15..bae6e02c 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.2, dev] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 + - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From 45760be12b5599e7bb5c8fd68efd78286d822bd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 11:25:53 +0000 Subject: [PATCH 248/260] Bump actions/checkout from 4.1.2 to 4.1.4 (dart-lang/async#271) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index bae6e02c..2f420cce 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.2, dev] steps: - - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b - uses: dart-lang/setup-dart@fedb1266e91cf51be2fdb382869461a434b920a3 with: sdk: ${{ matrix.sdk }} From 1e2f9c5f63c1d307b24747695cadad180bc9eb77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 May 2024 11:30:04 +0000 Subject: [PATCH 249/260] Bump dart-lang/setup-dart from 1.6.2 to 1.6.4 (dart-lang/async#272) 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.2 to 1.6.4.
Release notes

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

v1.6.4

  • Rebuild JS code to include changes from v1.6.3

v1.6.3

Changelog

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

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

  • 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

... (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.2&new-version=1.6.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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 2f420cce..2ba33ecc 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.2, 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 f5c5455a2228e6ca1f419384f3bc556fb263e979 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 3 May 2024 15:08:51 -0700 Subject: [PATCH 250/260] blast_repo fixes (dart-lang/async#273) dependabot --- pkgs/async/.github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/async/.github/dependabot.yml b/pkgs/async/.github/dependabot.yml index e802353f..5843b909 100644 --- a/pkgs/async/.github/dependabot.yml +++ b/pkgs/async/.github/dependabot.yml @@ -10,3 +10,7 @@ updates: interval: monthly labels: - autosubmit + groups: + github-actions: + patterns: + - "*" From 934d81c4c84f5535425d64a9ee9b036271ffe51e Mon Sep 17 00:00:00 2001 From: Sarah Zakarias Date: Wed, 15 May 2024 11:37:39 +0200 Subject: [PATCH 251/260] Add `topics` to `pubspec.yaml` (dart-lang/async#274) --- pkgs/async/pubspec.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index a7584221..b7f60ec9 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -15,3 +15,6 @@ dev_dependencies: fake_async: ^1.2.0 stack_trace: ^1.10.0 test: ^1.16.0 + +topics: +- async From ca3a1dbfaf4acfc92a3fa65bfde73cf6bf8d2665 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 28 May 2024 12:41:59 -0700 Subject: [PATCH 252/260] bump lints dep and fix (dart-lang/async#275) --- pkgs/async/lib/async.dart | 2 +- pkgs/async/pubspec.yaml | 2 +- pkgs/async/test/io_sink_impl.dart | 2 +- pkgs/async/test/sink_base_test.dart | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/lib/async.dart b/pkgs/async/lib/async.dart index 1d7b797c..67480e62 100644 --- a/pkgs/async/lib/async.dart +++ b/pkgs/async/lib/async.dart @@ -6,7 +6,7 @@ /// library. /// /// {@youtube 560 315 https://www.youtube.com/watch?v=r0tHiCjW2w0} -library async; +library; export 'src/async_cache.dart'; export 'src/async_memoizer.dart'; diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index b7f60ec9..0448d379 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: meta: ^1.1.7 dev_dependencies: - dart_flutter_team_lints: ^2.0.0 + dart_flutter_team_lints: ^3.0.0 fake_async: ^1.2.0 stack_trace: ^1.10.0 test: ^1.16.0 diff --git a/pkgs/async/test/io_sink_impl.dart b/pkgs/async/test/io_sink_impl.dart index 8fbf1c51..ccc23c22 100644 --- a/pkgs/async/test/io_sink_impl.dart +++ b/pkgs/async/test/io_sink_impl.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. @Deprecated('Tests deprecated functionality') -library io_sink_impl; +library; import 'dart:io'; diff --git a/pkgs/async/test/sink_base_test.dart b/pkgs/async/test/sink_base_test.dart index 9cf8c10d..ee324f51 100644 --- a/pkgs/async/test/sink_base_test.dart +++ b/pkgs/async/test/sink_base_test.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. @Deprecated('Tests deprecated functionality') -library sink_base_test; +library; import 'dart:async'; import 'dart:convert'; From f6346925e89a52f1880df2c1ad69f07291f49e26 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Jun 2024 17:22:06 +0000 Subject: [PATCH 253/260] Bump actions/checkout from 4.1.4 to 4.1.6 in the github-actions group (dart-lang/async#276) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 2ba33ecc..b60953e2 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.2, dev] steps: - - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 with: sdk: ${{ matrix.sdk }} From 9af10abafdcc7dc060cce46da99c1f3aa2f7beba Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 10 Jun 2024 09:31:11 -0700 Subject: [PATCH 254/260] Bump min SDK, tighten dependencies, test wasm on 3.4 (dart-lang/async#277) --- pkgs/async/.github/workflows/test-package.yml | 5 ++--- pkgs/async/CHANGELOG.md | 2 +- pkgs/async/pubspec.yaml | 6 +++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index b60953e2..33ab69f0 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.2, dev] + sdk: [3.4, dev] steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 - uses: dart-lang/setup-dart@f0ead981b4d9a35b37f30d36160575d60931ec30 @@ -62,5 +62,4 @@ jobs: - run: dart test --platform chrome --compiler dart2js if: always() && steps.install.outcome == 'success' - run: dart test --platform chrome --compiler dart2wasm - # TODO: drop `dev` filter when dart2wasm is working on stable - if: always() && steps.install.outcome == 'success' && matrix.sdk == 'dev' + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index b2b6f25b..c89049a6 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.12.0-wip -- Require Dart 3.2 +- Require Dart 3.4 ## 2.11.0 diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 0448d379..8726f0f6 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -4,17 +4,17 @@ description: Utility functions and classes related to the 'dart:async' library. repository: https://github.com/dart-lang/async environment: - sdk: ^3.2.0 + sdk: ^3.4.0 dependencies: collection: ^1.15.0 - meta: ^1.1.7 + meta: ^1.3.0 dev_dependencies: dart_flutter_team_lints: ^3.0.0 fake_async: ^1.2.0 stack_trace: ^1.10.0 - test: ^1.16.0 + test: ^1.16.6 topics: - async From 9383a3c0ac250c796a5c37272aab72ee20025276 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Mon, 17 Jun 2024 11:15:11 -0700 Subject: [PATCH 255/260] Drop use of pkg:collection whereNotNull() (dart-lang/async#278) Exists in SDK as of v3.0 --- pkgs/async/lib/src/cancelable_operation.dart | 4 +--- pkgs/async/lib/src/stream_group.dart | 4 +--- pkgs/async/lib/src/stream_queue.dart | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/pkgs/async/lib/src/cancelable_operation.dart b/pkgs/async/lib/src/cancelable_operation.dart index bb59f60e..2610613c 100644 --- a/pkgs/async/lib/src/cancelable_operation.dart +++ b/pkgs/async/lib/src/cancelable_operation.dart @@ -4,8 +4,6 @@ import 'dart:async'; -import 'package:collection/collection.dart'; - /// An asynchronous operation that can be cancelled. /// /// The value of this operation is exposed as [value]. When this operation is @@ -521,7 +519,7 @@ class CancelableCompleter { final isFuture = toReturn is Future; final cancelFutures = >[ if (isFuture) toReturn, - ...?_cancelForwarders?.map(_forward).whereNotNull() + ...?_cancelForwarders?.map(_forward).nonNulls ]; final results = (isFuture && cancelFutures.length == 1) ? [await toReturn] diff --git a/pkgs/async/lib/src/stream_group.dart b/pkgs/async/lib/src/stream_group.dart index 78912c9f..502a111c 100644 --- a/pkgs/async/lib/src/stream_group.dart +++ b/pkgs/async/lib/src/stream_group.dart @@ -4,8 +4,6 @@ import 'dart:async'; -import 'package:collection/collection.dart'; - /// A collection of streams whose events are unified and sent through a central /// stream. /// @@ -239,7 +237,7 @@ class StreamGroup implements Sink> { return null; } }) - .whereNotNull() + .nonNulls .toList(); _subscriptions.clear(); diff --git a/pkgs/async/lib/src/stream_queue.dart b/pkgs/async/lib/src/stream_queue.dart index f7ab8bad..c5c0c196 100644 --- a/pkgs/async/lib/src/stream_queue.dart +++ b/pkgs/async/lib/src/stream_queue.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'dart:collection'; -import 'package:collection/collection.dart'; +import 'package:collection/collection.dart' show QueueList; import 'cancelable_operation.dart'; import 'result/result.dart'; From a44670754b8b63cacccd3b4f45144f1859aab8cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:30:05 +0000 Subject: [PATCH 256/260] Bump the github-actions group with 2 updates (dart-lang/async#280) 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/async#118: dart-lang/setup-dartdart-lang/async#118

Changelog

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

v1.6.5

dart-lang/async#118: dart-lang/setup-dartdart-lang/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/async/.github/workflows/test-package.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 33ab69f0..96fe8172 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.4, 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 c156a342118d4a8ac74d3df5a759997275e9f5a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:24:44 +0000 Subject: [PATCH 257/260] Bump actions/checkout from 4.1.7 to 4.2.0 in the github-actions group (dart-lang/async#286) 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/async/.github/workflows/test-package.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/async/.github/workflows/test-package.yml b/pkgs/async/.github/workflows/test-package.yml index 96fe8172..2f64b2b4 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/pkgs/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.4, dev] steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 + - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 - uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672 with: sdk: ${{ matrix.sdk }} From c2125f49aaee0b3d04f07c38f80752286246a113 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 14 Oct 2024 16:27:54 +0200 Subject: [PATCH 258/260] Moving fixes --- .github/labeler.yaml | 4 ++++ .../workflows/async.yaml | 14 ++++++++++++-- README.md | 4 ++-- pkgs/async/.github/dependabot.yml | 16 ---------------- pkgs/async/.github/workflows/publish.yaml | 14 -------------- pkgs/async/CHANGELOG.md | 7 ++++--- pkgs/async/README.md | 2 +- pkgs/async/pubspec.yaml | 4 ++-- pkgs/placeholder | 0 9 files changed, 25 insertions(+), 40 deletions(-) rename pkgs/async/.github/workflows/test-package.yml => .github/workflows/async.yaml (88%) delete mode 100644 pkgs/async/.github/dependabot.yml delete mode 100644 pkgs/async/.github/workflows/publish.yaml delete mode 100644 pkgs/placeholder diff --git a/.github/labeler.yaml b/.github/labeler.yaml index 074dd1f4..597bd488 100644 --- a/.github/labeler.yaml +++ b/.github/labeler.yaml @@ -3,3 +3,7 @@ "package-args": - changed-files: - any-glob-to-any-file: 'pkgs/args/**' + +"package-async": + - changed-files: + - any-glob-to-any-file: 'pkgs/async/**' diff --git a/pkgs/async/.github/workflows/test-package.yml b/.github/workflows/async.yaml similarity index 88% rename from pkgs/async/.github/workflows/test-package.yml rename to .github/workflows/async.yaml index 2f64b2b4..7d3ad105 100644 --- a/pkgs/async/.github/workflows/test-package.yml +++ b/.github/workflows/async.yaml @@ -3,15 +3,25 @@ name: Dart CI on: # Run on PRs and pushes to the default branch. push: - branches: [ master ] + branches: [ main ] + paths: + - '.github/workflows/async.yaml' + - 'pkgs/async/**' pull_request: - branches: [ master ] + branches: [ main ] + paths: + - '.github/workflows/async.yaml' + - 'pkgs/async/**' schedule: - cron: "0 0 * * 0" env: PUB_ENVIRONMENT: bot.github +defaults: + run: + working-directory: pkgs/async/ + jobs: # Check code formatting and static analysis on a single OS (linux) # against Dart dev. diff --git a/README.md b/README.md index 7b81d0b4..ffe157db 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ This repository is home to various Dart packages under the [dart.dev](https://pu | Package | Description | Version | |---|---|---| -| [args](pkgs/args/) | Library for defining parsers for parsing raw command-line arguments into a set - of options and values. | [![pub package](https://img.shields.io/pub/v/args.svg)](https://pub.dev/packages/args) | +| [args](pkgs/args/) | Library for defining parsers for parsing raw command-line arguments into a set of options and values. | [![pub package](https://img.shields.io/pub/v/args.svg)](https://pub.dev/packages/args) | +| [async](pkgs/async/) | Utility functions and classes related to the 'dart:async' library.| [![pub package](https://img.shields.io/pub/v/async.svg)](https://pub.dev/packages/async) | ## Publishing automation diff --git a/pkgs/async/.github/dependabot.yml b/pkgs/async/.github/dependabot.yml deleted file mode 100644 index 5843b909..00000000 --- a/pkgs/async/.github/dependabot.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Set update schedule for GitHub Actions -# See https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot - -version: 2 -updates: - -- package-ecosystem: github-actions - directory: / - schedule: - interval: monthly - labels: - - autosubmit - groups: - github-actions: - patterns: - - "*" diff --git a/pkgs/async/.github/workflows/publish.yaml b/pkgs/async/.github/workflows/publish.yaml deleted file mode 100644 index fcb7ccb8..00000000 --- a/pkgs/async/.github/workflows/publish.yaml +++ /dev/null @@ -1,14 +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 diff --git a/pkgs/async/CHANGELOG.md b/pkgs/async/CHANGELOG.md index c89049a6..06ac7d11 100644 --- a/pkgs/async/CHANGELOG.md +++ b/pkgs/async/CHANGELOG.md @@ -1,6 +1,7 @@ -## 2.12.0-wip +## 2.12.0 -- Require Dart 3.4 +- Require Dart 3.4. +- Move to `dart-lang/core` monorepo. ## 2.11.0 @@ -13,7 +14,7 @@ * Add `CancelableOperation.thenOperation` which gives more flexibility to complete the resulting operation. * Add `CancelableCompleter.completeOperation`. -* Require Dart 2.18 +* Require Dart 2.18. ## 2.9.0 diff --git a/pkgs/async/README.md b/pkgs/async/README.md index be9ed20d..cb074d03 100644 --- a/pkgs/async/README.md +++ b/pkgs/async/README.md @@ -1,4 +1,4 @@ -[![Dart CI](https://github.com/dart-lang/async/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/async/actions/workflows/test-package.yml) +[![Dart CI](https://github.com/dart-lang/core/actions/workflows/async.yaml/badge.svg)](https://github.com/dart-lang/core/actions/workflows/async.yaml) [![pub package](https://img.shields.io/pub/v/async.svg)](https://pub.dev/packages/async) [![package publisher](https://img.shields.io/pub/publisher/async.svg)](https://pub.dev/packages/async/publisher) diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index 8726f0f6..e28aff6b 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,7 +1,7 @@ name: async -version: 2.12.0-wip +version: 2.12.0 description: Utility functions and classes related to the 'dart:async' library. -repository: https://github.com/dart-lang/async +repository: https://github.com/dart-lang/core/main/pkgs/async environment: sdk: ^3.4.0 diff --git a/pkgs/placeholder b/pkgs/placeholder deleted file mode 100644 index e69de29b..00000000 From f5fbcddfcd047387a1d48670ca2c6467b648182b Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 15 Oct 2024 08:08:06 +0200 Subject: [PATCH 259/260] Rename labeler --- .github/{labeler.yaml => labeler.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{labeler.yaml => labeler.yml} (100%) diff --git a/.github/labeler.yaml b/.github/labeler.yml similarity index 100% rename from .github/labeler.yaml rename to .github/labeler.yml From b3264f88615e69ac10788326a169df1212f6db9b Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 16 Oct 2024 10:33:02 +0200 Subject: [PATCH 260/260] Changes as per review --- .github/labeler.yml | 2 +- .github/workflows/async.yaml | 2 +- pkgs/async/CONTRIBUTING.md | 33 --------------------------------- pkgs/async/pubspec.yaml | 8 ++++---- 4 files changed, 6 insertions(+), 39 deletions(-) delete mode 100644 pkgs/async/CONTRIBUTING.md diff --git a/.github/labeler.yml b/.github/labeler.yml index 1b97e28a..fc4f3b41 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,6 +4,6 @@ - changed-files: - any-glob-to-any-file: 'pkgs/args/**' -"package-async": +"package:async": - changed-files: - any-glob-to-any-file: 'pkgs/async/**' diff --git a/.github/workflows/async.yaml b/.github/workflows/async.yaml index 7d3ad105..025bfde0 100644 --- a/.github/workflows/async.yaml +++ b/.github/workflows/async.yaml @@ -1,4 +1,4 @@ -name: Dart CI +name: package:async on: # Run on PRs and pushes to the default branch. diff --git a/pkgs/async/CONTRIBUTING.md b/pkgs/async/CONTRIBUTING.md deleted file mode 100644 index 6f5e0ea6..00000000 --- a/pkgs/async/CONTRIBUTING.md +++ /dev/null @@ -1,33 +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](https://cla.developers.google.com/about/google-individual) -(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. - -### Code reviews -All submissions, including submissions by project members, require review. - -### File headers -All files in the project must start with the following header. - - // Copyright (c) 2015, 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](https://developers.google.com/open-source/cla/corporate). diff --git a/pkgs/async/pubspec.yaml b/pkgs/async/pubspec.yaml index e28aff6b..73c26c53 100644 --- a/pkgs/async/pubspec.yaml +++ b/pkgs/async/pubspec.yaml @@ -1,7 +1,10 @@ name: async version: 2.12.0 description: Utility functions and classes related to the 'dart:async' library. -repository: https://github.com/dart-lang/core/main/pkgs/async +repository: https://github.com/dart-lang/core/tree/main/pkgs/async + +topics: + - async environment: sdk: ^3.4.0 @@ -15,6 +18,3 @@ dev_dependencies: fake_async: ^1.2.0 stack_trace: ^1.10.0 test: ^1.16.6 - -topics: -- async