| // 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'; |
| |
| import 'utils.dart'; |
| |
| void main() { |
| late FutureGroup futureGroup; |
| setUp(() { |
| futureGroup = FutureGroup(); |
| }); |
| |
| group('with no futures', () { |
| test('never completes if nothing happens', () async { |
| var completed = false; |
| futureGroup.future.then((_) => completed = true); |
| |
| await flushMicrotasks(); |
| 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(Future.value()); |
| await flushMicrotasks(); |
| |
| var completed = false; |
| futureGroup.future.then((_) => completed = true); |
| |
| await flushMicrotasks(); |
| expect(completed, isFalse); |
| }); |
| |
| test("completes once it's closed", () async { |
| futureGroup.add(Future.value()); |
| await flushMicrotasks(); |
| |
| expect(futureGroup.future, completes); |
| futureGroup.close(); |
| }); |
| |
| test("completes to that future's value", () { |
| 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(Future.error('error')); |
| expect(futureGroup.future, throwsA('error')); |
| }); |
| }); |
| |
| test('completes once all contained futures complete', () async { |
| var completer1 = Completer(); |
| var completer2 = Completer(); |
| var completer3 = 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 flushMicrotasks(); |
| expect(completed, isFalse); |
| |
| completer2.complete(); |
| await flushMicrotasks(); |
| expect(completed, isFalse); |
| |
| completer3.complete(); |
| await flushMicrotasks(); |
| expect(completed, isTrue); |
| }); |
| |
| test('completes to the values of the futures in order of addition', () { |
| var completer1 = Completer(); |
| var completer2 = Completer(); |
| var completer3 = 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 completer1 = Completer(); |
| var completer2 = Completer(); |
| var completer3 = 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')); |
| }); |
| |
| group('onIdle:', () { |
| test('emits an event when the last pending future completes', () async { |
| var idle = false; |
| futureGroup.onIdle.listen((_) => idle = true); |
| |
| var completer1 = Completer(); |
| var completer2 = Completer(); |
| var completer3 = 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 = Completer(); |
| futureGroup.add(completer.future); |
| |
| completer.complete(); |
| await flushMicrotasks(); |
| expect(idle, isTrue); |
| expect(futureGroup.isIdle, isTrue); |
| |
| idle = false; |
| completer = 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(expectAsync1((_) { |
| expect(futureFired, isFalse); |
| idle = true; |
| }), onDone: expectAsync0(() { |
| expect(idle, isTrue); |
| expect(futureFired, isFalse); |
| onIdleDone = true; |
| })); |
| |
| futureGroup.future.then(expectAsync1((_) { |
| expect(idle, isTrue); |
| expect(onIdleDone, isTrue); |
| futureFired = true; |
| })); |
| |
| var completer = 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); |
| }); |
| }); |
| } |