Add methods to Result. Bump version to 2.0 and remove deprecated features. R=floitsch@google.com Review-Url: https://codereview.chromium.org//2996143002 .
diff --git a/CHANGELOG.md b/CHANGELOG.md index e4a8696..a1e9683 100644 --- a/CHANGELOG.md +++ b/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<Null>`. + 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/lib/async.dart b/lib/async.dart index e090032..25fc0fd 100644 --- a/lib/async.dart +++ b/lib/async.dart
@@ -17,11 +17,9 @@ 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/lib/result.dart b/lib/result.dart deleted file mode 100644 index 627f200..0000000 --- a/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/lib/src/result.dart b/lib/src/result.dart deleted file mode 100644 index 49457c3..0000000 --- a/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<T> { - /// 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<Object, Result> 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, Result> 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<Object, Result> 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, Result> 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<T>; - - /// 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<Result<T>> capture<T>(Future<T> future) { - return future.then((value) => new ValueResult(value), - onError: (error, stackTrace) => new ErrorResult<T>(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<T> release<T>(Future<Result<T>> future) => - future.then<T>((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<Result<T>> captureStream<T>(Stream<T> source) => - source.transform(new CaptureStreamTransformer<T>()); - - /// 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<T> releaseStream<T>(Stream<Result<T>> source) => - source.transform(new ReleaseStreamTransformer<T>()); - - /// 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<T> flatten<T>(Result<Result<T>> result) { - if (result.isValue) return result.asValue.value; - return new ErrorResult<T>(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<T> get asValue; - - /// If this is an error result, return itself. - /// - /// Otherwise return `null`. - ErrorResult<T> get asError; - - /// Complete a completer with this result. - void complete(Completer<T> completer); - - /// Add this result to an [EventSink]. - /// - /// Calls the sink's `add` or `addError` method as appropriate. - void addTo(EventSink<T> sink); - - /// Creates a future completed with this result as a value or an error. - Future<T> get asFuture; -}
diff --git a/lib/src/result/capture_sink.dart b/lib/src/result/capture_sink.dart index 7474525..c85b553 100644 --- a/lib/src/result/capture_sink.dart +++ b/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<T> implements EventSink<T> { - final EventSink _sink; + final EventSink<Result<T>> _sink; CaptureSink(EventSink<Result<T>> sink) : _sink = sink;
diff --git a/lib/src/result/capture_transformer.dart b/lib/src/result/capture_transformer.dart index 6b1bbf2..71e8a00 100644 --- a/lib/src/result/capture_transformer.dart +++ b/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<T> implements StreamTransformer<T, Result<T>> { const CaptureStreamTransformer(); @@ -19,7 +18,6 @@ return new Stream<Result<T>>.eventTransformed(source, _createSink); } - static EventSink _createSink(EventSink<Result> sink) { - return new CaptureSink(sink); - } + // Since Stream.eventTransformed is not generic, this method can be static. + static EventSink _createSink(EventSink<Result> sink) => new CaptureSink(sink); }
diff --git a/lib/src/result/error.dart b/lib/src/result/error.dart index b6d5859..45e06a2 100644 --- a/lib/src/result/error.dart +++ b/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<T> implements Result<T> { +class ErrorResult implements Result<Null> { + /// 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<T> get asValue => null; - ErrorResult<T> get asError => this; + ValueResult<Null> get asValue => null; + ErrorResult get asError => this; ErrorResult(this.error, this.stackTrace); @@ -27,7 +30,7 @@ sink.addError(error, stackTrace); } - Future<T> get asFuture => new Future.error(error, stackTrace); + Future<Null> get asFuture => new Future<Null>.error(error, stackTrace); /// Calls an error handler with the error and stacktrace. /// @@ -42,4 +45,12 @@ 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/lib/src/result/future.dart b/lib/src/result/future.dart index 749b101..ff30546 100644 --- a/lib/src/result/future.dart +++ b/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 @@ Result<T> get result => _result; Result<T> _result; - factory ResultFuture(Future<T> future) { - ResultFuture<T> resultFuture; - resultFuture = new ResultFuture._(() async { - var result = await Result.capture(future); - resultFuture._result = result; - return await result.asFuture; - }()); - return resultFuture; + ResultFuture(Future<T> future) : super(future) { + Result.capture(future).then((result) { + _result = result; + }); } - - ResultFuture._(Future<T> future) : super(future); }
diff --git a/lib/src/result/release_sink.dart b/lib/src/result/release_sink.dart index 114981b..a0715a1 100644 --- a/lib/src/result/release_sink.dart +++ b/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<T> implements EventSink<Result<T>> { final EventSink _sink; ReleaseSink(EventSink<T> sink) : _sink = sink; void add(Result<T> 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/lib/src/result/release_transformer.dart b/lib/src/result/release_transformer.dart index 456ed0a..fc9278d 100644 --- a/lib/src/result/release_transformer.dart +++ b/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<T> implements StreamTransformer<Result<T>, T> { const ReleaseStreamTransformer(); @@ -16,7 +15,6 @@ return new Stream<T>.eventTransformed(source, _createSink); } - static EventSink<Result> _createSink(EventSink sink) { - return new ReleaseSink(sink); - } + // Since Stream.eventTransformed is not generic, this method can be static. + static EventSink<Result> _createSink(EventSink sink) => new ReleaseSink(sink); }
diff --git a/lib/src/result/result.dart b/lib/src/result/result.dart new file mode 100644 index 0000000..98b5aa4 --- /dev/null +++ b/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<T> { + /// 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<Object, Result<Object>> + captureStreamTransformer = const CaptureStreamTransformer<Object>(); + + /// 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<Result<Object>, Object> + releaseStreamTransformer = const ReleaseStreamTransformer<Object>(); + + /// 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<Object, Result<Object>> + captureSinkTransformer = + const StreamSinkTransformer<Object, Result<Object>>.fromStreamTransformer( + const CaptureStreamTransformer<Object>()); + + /// 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<Result<Object>, Object> + releaseSinkTransformer = + const StreamSinkTransformer<Result<Object>, Object>.fromStreamTransformer( + const ReleaseStreamTransformer<Object>()); + + /// 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<T>(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<T>; + + /// 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<Result<T>> capture<T>(Future<T> 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<List<Result<T>>> captureAll<T>(Iterable<FutureOr<T>> elements) { + var results = <Result<T>>[]; + int pending = 0; + var completer; + for (var element in elements) { + if (element is Future<T>) { + int i = results.length; + results.add(null); + pending++; + Result.capture<T>(element).then((result) { + results[i] = result; + if (--pending == 0) { + completer.complete(results); + } + }); + } else { + results.add(new Result<T>.value(element)); + } + } + if (pending == 0) { + return new Future<List<Result<T>>>.value(results); + } + completer = new Completer<List<Result<T>>>(); + 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<T> release<T>(Future<Result<T>> future) => + future.then<T>((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<Result<T>> captureStream<T>(Stream<T> source) => + source.transform(new CaptureStreamTransformer<T>()); + + /// 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<T> releaseStream<T>(Stream<Result<T>> source) => + source.transform(new ReleaseStreamTransformer<T>()); + + /// 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<Result<T>> releaseSink<T>(EventSink<T> sink) => + new ReleaseSink<T>(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<T> captureSink<T>(EventSink<Result<T>> sink) => + new CaptureSink<T>(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<T> flatten<T>(Result<Result<T>> 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<List<T>> flattenAll<T>(Iterable<Result<T>> results) { + var values = <T>[]; + for (var result in results) { + if (result.isValue) { + values.add(result.asValue.value); + } else { + return result.asError; + } + } + return new Result<List<T>>.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<T> 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<T> completer); + + /// Adds this result to an [EventSink]. + /// + /// Calls the sink's `add` or `addError` method as appropriate. + void addTo(EventSink<T> sink); + + /// A future that has been completed with this result as a value or an error. + Future<T> get asFuture; +}
diff --git a/lib/src/result/value.dart b/lib/src/result/value.dart index 39fa048..edc41fb 100644 --- a/lib/src/result/value.dart +++ b/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<T> implements Result<T> { + /// The result of a successful computation. final T value; bool get isValue => true; bool get isError => false; ValueResult<T> get asValue => this; - ErrorResult<T> get asError => null; + ErrorResult get asError => null; ValueResult(this.value); @@ -27,4 +28,9 @@ } Future<T> 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/lib/src/stream_queue.dart b/lib/src/stream_queue.dart index b9023ab..1318114 100644 --- a/lib/src/stream_queue.dart +++ b/lib/src/stream_queue.dart
@@ -8,7 +8,7 @@ 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 @@ /// source subscription. class _CancelRequest<T> implements _EventRequest<T> { /// Completer for the future returned by the `cancel` call. + /// TODO(lrn); make this Completer<void> 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/lib/src/stream_splitter.dart b/lib/src/stream_splitter.dart index 6cec98c..ac4260d 100644 --- a/lib/src/stream_splitter.dart +++ b/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/lib/stream_zip.dart b/lib/stream_zip.dart deleted file mode 100644 index cdeb5c9..0000000 --- a/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/pubspec.yaml b/pubspec.yaml index 2ee2505..bb41d4f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -1,5 +1,5 @@ name: async -version: 1.13.3 +version: 2.0.0 author: Dart Team <misc@dartlang.org> description: Utility functions and classes related to the 'dart:async' library. homepage: https://www.github.com/dart-lang/async
diff --git a/test/result/result_captureAll_test.dart b/test/result/result_captureAll_test.dart new file mode 100644 index 0000000..172e38a --- /dev/null +++ b/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<int> res(int n) => new Result<int>.value(n); +Result err(n) => new ErrorResult("$n", someStack); + +/// Helper function creating an iterable of futures. +Iterable<Future<int>> futures(int count, {bool throwWhen(int index)}) sync* { + for (int i = 0; i < count; i++) { + if (throwWhen != null && throwWhen(i)) { + yield new Future<int>.error("$i", someStack); + } else { + yield new Future<int>.value(i); + } + } +} + +main() { + test("empty", () async { + var all = await Result.captureAll<int>(futures(0)); + expect(all, []); + }); + + group("futures only,", () { + test("single", () async { + var all = await Result.captureAll<int>(futures(1)); + expect(all, [res(0)]); + }); + + test("multiple", () async { + var all = await Result.captureAll<int>(futures(3)); + expect(all, [res(0), res(1), res(2)]); + }); + + test("error only", () async { + var all = + await Result.captureAll<int>(futures(1, throwWhen: (_) => true)); + expect(all, [err(0)]); + }); + + test("multiple error only", () async { + var all = + await Result.captureAll<int>(futures(3, throwWhen: (_) => true)); + expect(all, [err(0), err(1), err(2)]); + }); + + test("mixed error and value", () async { + var all = + await Result.captureAll<int>(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<int>()); + var all = Result.captureAll<int>(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<int>()); + var all = Result.captureAll<int>(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<int>()); + var all = Result.captureAll<int>(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<int>()); + var all = Result.captureAll<int>(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<int>()); + var all = Result.captureAll<int>(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<int>()); + var all = Result.captureAll<int>(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<<n) won't work. + test("randomized #$n seed:${seed.toRadixString(16)}", () async { + var cs = new List.generate(n, (_) => new Completer<int>()); + var all = Result.captureAll<int>(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<Function()>.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<int>(<int>[1]); + expect(all, [res(1)]); + }); + test("multiple", () async { + var all = await Result.captureAll<int>(<int>[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<int>(<FutureOr<int>>[ + 1, + new Future<int>(() => 2), + 3, + new Future<int>.value(4), + ]); + expect(all, [res(1), res(2), res(3), res(4)]); + }); + test("error", () async { + var all = await Result.captureAll<int>(<FutureOr<int>>[ + 1, + new Future<int>(() => 2), + 3, + new Future<int>(() async => await new Future.error("4", someStack)), + new Future<int>.value(5) + ]); + expect(all, [res(1), res(2), res(3), err(4), res(5)]); + }); + }); +}
diff --git a/test/result/result_flattenAll_test.dart b/test/result/result_flattenAll_test.dart new file mode 100644 index 0000000..caaf668 --- /dev/null +++ b/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<T> res<T>(T n) => new Result<T>.value(n); +Result err(n) => new ErrorResult("$n", someStack); + +/// Helper function creating an iterable of results. +Iterable<Result<int>> 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<int>(results(0)), res([])); + }); + test("single value", () { + expectAll(Result.flattenAll<int>(results(1)), res([0])); + }); + test("single error", () { + expectAll( + Result.flattenAll<int>(results(1, throwWhen: (_) => true)), err(0)); + }); + test("multiple values", () { + expectAll(Result.flattenAll<int>(results(5)), res([0, 1, 2, 3, 4])); + }); + test("multiple errors", () { + expectAll(Result.flattenAll<int>(results(5, throwWhen: (x) => x.isOdd)), + err(1)); // First error is result. + }); + test("error last", () { + expectAll( + Result.flattenAll<int>(results(5, throwWhen: (x) => x == 4)), err(4)); + }); +}
diff --git a/test/result_future_test.dart b/test/result/result_future_test.dart similarity index 100% rename from test/result_future_test.dart rename to test/result/result_future_test.dart
diff --git a/test/result_test.dart b/test/result/result_test.dart similarity index 100% rename from test/result_test.dart rename to test/result/result_test.dart