| // Copyright (c) 2014, 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 test.cancelable_future; |
| |
| import 'dart:async'; |
| |
| import 'package:analyzer/src/cancelable_future.dart'; |
| import 'package:unittest/unittest.dart'; |
| import 'package:watcher/src/utils.dart'; |
| |
| import 'reflective_tests.dart'; |
| import 'utils.dart'; |
| |
| void main() { |
| initializeTestEnvironment(); |
| runReflectiveTests(CancelableCompleterTests); |
| runReflectiveTests(CancelableFutureTests); |
| } |
| |
| @reflectiveTest |
| class CancelableCompleterTests { |
| CancelableCompleter<Object> completer; |
| int cancelCount = 0; |
| |
| void setUp() { |
| completer = new CancelableCompleter<Object>(() { |
| cancelCount++; |
| }); |
| } |
| |
| Future test_cancel_after_cancel() { |
| // It is permissible to cancel multiple times, but only the first |
| // cancellation has any effect. |
| expect(cancelCount, 0); |
| completer.future.cancel(); |
| expect(cancelCount, 1); |
| completer.future.cancel(); |
| expect(cancelCount, 1); |
| // Make sure the future still completes with error. |
| return completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(error, new isInstanceOf<FutureCanceledError>()); |
| // And make sure nothing else happens. |
| }).then((_) => pumpEventQueue()).then((_) { |
| expect(completer.isCompleted, isFalse); |
| expect(cancelCount, 1); |
| }); |
| } |
| |
| Future test_cancel_after_chaining() { |
| bool callbackInvoked = false; |
| completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(callbackInvoked, isFalse); |
| expect(error, new isInstanceOf<FutureCanceledError>()); |
| callbackInvoked = true; |
| }); |
| expect(cancelCount, 0); |
| completer.future.cancel(); |
| // The cancel callback should have been invoked immediately. |
| expect(cancelCount, 1); |
| // But the completer should remain in the "not completed" state. |
| expect(completer.isCompleted, isFalse); |
| // The callback should be deferred to a microtask. |
| expect(callbackInvoked, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| expect(completer.isCompleted, isFalse); |
| expect(cancelCount, 1); |
| }); |
| } |
| |
| Future test_cancel_after_complete() { |
| Object obj = new Object(); |
| completer.complete(obj); |
| completer.future.cancel(); |
| // The cancel callback should not have been invoked, because it was too |
| // late to cancel. |
| expect(cancelCount, 0); |
| // Make sure the future still completes with the object. |
| return completer.future.then((result) { |
| expect(result, same(obj)); |
| // And make sure nothing else happens. |
| }).then((_) => pumpEventQueue()).then((_) { |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 0); |
| }); |
| } |
| |
| Future test_cancel_before_chaining() { |
| completer.future.cancel(); |
| // The cancel callback should have been invoked immediately. |
| expect(cancelCount, 1); |
| // But the completer should remain in the "not completed" state. |
| expect(completer.isCompleted, isFalse); |
| bool callbackInvoked = false; |
| completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(callbackInvoked, isFalse); |
| expect(error, new isInstanceOf<FutureCanceledError>()); |
| callbackInvoked = true; |
| }); |
| // The callback should be deferred to a microtask. |
| expect(callbackInvoked, isFalse); |
| expect(completer.isCompleted, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| expect(completer.isCompleted, isFalse); |
| expect(cancelCount, 1); |
| }); |
| } |
| |
| Future test_complete_after_cancel() { |
| completer.future.cancel(); |
| // The cancel callback should have been invoked immediately. |
| expect(cancelCount, 1); |
| // Completing should have no effect other than to set the isCompleted |
| // flag. |
| expect(completer.isCompleted, isFalse); |
| Object obj = new Object(); |
| completer.complete(obj); |
| expect(completer.isCompleted, isTrue); |
| // Make sure the future still completer with error. |
| return completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(error, new isInstanceOf<FutureCanceledError>()); |
| // And make sure nothing else happens. |
| }).then((_) => pumpEventQueue()).then((_) { |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 1); |
| }); |
| } |
| |
| Future test_complete_after_chaining() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| completer.future.then((result) { |
| expect(callbackInvoked, isFalse); |
| expect(result, same(obj)); |
| callbackInvoked = true; |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| expect(completer.isCompleted, isFalse); |
| // Running the event loop should have no effect since the completer hasn't |
| // been completed yet. |
| return pumpEventQueue().then((_) { |
| completer.complete(obj); |
| expect(completer.isCompleted, isTrue); |
| // The callback should be deferred to a microtask. |
| expect(callbackInvoked, isFalse); |
| }).then((_) => pumpEventQueue()).then((_) { |
| expect(callbackInvoked, isTrue); |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 0); |
| }); |
| } |
| |
| void test_complete_after_complete() { |
| // As with an ordinary Completer, calling complete() (or completeError) |
| // after calling complete() should throw an exception. |
| completer.complete(); |
| expect(() { |
| completer.complete(); |
| }, throws); |
| expect(() { |
| completer.completeError(new Object()); |
| }, throws); |
| } |
| |
| void test_complete_after_completeError() { |
| // As with an ordinary Completer, calling complete() (or completeError) |
| // after calling completeError() should throw an exception. |
| completer.completeError(new Object()); |
| expect(() { |
| completer.complete(); |
| }, throws); |
| expect(() { |
| completer.completeError(new Object()); |
| }, throws); |
| // Now absorb the error that's in the completer's future. |
| completer.future.catchError((_) => null); |
| } |
| |
| Future test_complete_before_chaining() { |
| Object obj = new Object(); |
| completer.complete(obj); |
| expect(completer.isCompleted, isTrue); |
| bool callbackInvoked = false; |
| completer.future.then((result) { |
| expect(callbackInvoked, isFalse); |
| expect(result, same(obj)); |
| callbackInvoked = true; |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| // The callback should be deferred to a microtask. |
| expect(callbackInvoked, isFalse); |
| expect(completer.isCompleted, isTrue); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 0); |
| }); |
| } |
| |
| Future test_completeError_after_cancel() { |
| completer.future.cancel(); |
| // The cancel callback should have been invoked immediately. |
| expect(cancelCount, 1); |
| // Completing should have no effect other than to set the isCompleted |
| // flag. |
| expect(completer.isCompleted, isFalse); |
| Object obj = new Object(); |
| completer.completeError(obj); |
| expect(completer.isCompleted, isTrue); |
| // Make sure the future still completes with error. |
| return completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(error, new isInstanceOf<FutureCanceledError>()); |
| // And make sure nothing else happens. |
| }).then((_) => pumpEventQueue()).then((_) { |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 1); |
| }); |
| } |
| |
| Future test_completeError_after_chaining() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(callbackInvoked, isFalse); |
| expect(error, same(obj)); |
| callbackInvoked = true; |
| }); |
| expect(completer.isCompleted, isFalse); |
| // Running the event loop should have no effect since the completer hasn't |
| // been completed yet. |
| return pumpEventQueue().then((_) { |
| completer.completeError(obj); |
| expect(completer.isCompleted, isTrue); |
| // The callback should be deferred to a microtask. |
| expect(callbackInvoked, isFalse); |
| }).then((_) => pumpEventQueue()).then((_) { |
| expect(callbackInvoked, isTrue); |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 0); |
| }); |
| } |
| |
| Future test_completeError_before_chaining() { |
| Object obj = new Object(); |
| completer.completeError(obj); |
| expect(completer.isCompleted, isTrue); |
| bool callbackInvoked = false; |
| completer.future.then((_) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(callbackInvoked, isFalse); |
| expect(error, same(obj)); |
| callbackInvoked = true; |
| }); |
| // The callback should be deferred to a microtask. |
| expect(callbackInvoked, isFalse); |
| expect(completer.isCompleted, isTrue); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| expect(completer.isCompleted, isTrue); |
| expect(cancelCount, 0); |
| }); |
| } |
| |
| void test_initialState() { |
| expect(completer.isCompleted, isFalse); |
| expect(cancelCount, 0); |
| } |
| } |
| |
| @reflectiveTest |
| class CancelableFutureTests { |
| Future test_defaultConstructor_returnFuture() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| new CancelableFuture(() => new Future(() => obj)).then((result) { |
| expect(callbackInvoked, isFalse); |
| expect(result, same(obj)); |
| callbackInvoked = true; |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| expect(callbackInvoked, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| }); |
| } |
| |
| Future test_defaultConstructor_returnValue() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| new CancelableFuture(() => obj).then((result) { |
| expect(callbackInvoked, isFalse); |
| expect(result, same(obj)); |
| callbackInvoked = true; |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| expect(callbackInvoked, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| }); |
| } |
| |
| Future test_defaultConstructor_throwException() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| new CancelableFuture(() { |
| throw obj; |
| }).then((result) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(callbackInvoked, isFalse); |
| expect(error, same(obj)); |
| callbackInvoked = true; |
| }); |
| expect(callbackInvoked, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| }); |
| } |
| |
| Future test_delayed_noCallback() { |
| DateTime start = new DateTime.now(); |
| return new CancelableFuture.delayed(new Duration(seconds: 1)) |
| .then((result) { |
| DateTime end = new DateTime.now(); |
| expect(result, isNull); |
| expect(end.difference(start).inMilliseconds > 900, isTrue); |
| }); |
| } |
| |
| Future test_delayed_withCallback() { |
| Object obj = new Object(); |
| DateTime start = new DateTime.now(); |
| return new CancelableFuture.delayed(new Duration(seconds: 1), () { |
| DateTime end = new DateTime.now(); |
| expect(end.difference(start).inMilliseconds > 900, isTrue); |
| return obj; |
| }).then((result) { |
| expect(result, same(obj)); |
| }); |
| } |
| |
| Future test_error() { |
| Object obj = new Object(); |
| return new CancelableFuture.error(obj).then((result) { |
| fail('Expected error completion'); |
| }, onError: (error) { |
| expect(error, same(obj)); |
| }); |
| } |
| |
| Future test_microtask() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| new CancelableFuture.microtask(() => obj).then((result) { |
| expect(callbackInvoked, isFalse); |
| expect(result, same(obj)); |
| callbackInvoked = true; |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| expect(callbackInvoked, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| }); |
| } |
| |
| Future test_sync() { |
| Object obj = new Object(); |
| bool callbackInvoked = false; |
| new CancelableFuture.sync(() => obj).then((result) { |
| expect(callbackInvoked, isFalse); |
| expect(result, same(obj)); |
| callbackInvoked = true; |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| expect(callbackInvoked, isFalse); |
| return pumpEventQueue().then((_) { |
| expect(callbackInvoked, isTrue); |
| }); |
| } |
| |
| Future test_value() { |
| Object obj = new Object(); |
| return new CancelableFuture.value(obj).then((result) { |
| expect(result, same(obj)); |
| }, onError: (error) { |
| fail('Expected successful completion'); |
| }); |
| } |
| } |