Index: packages/analyzer/test/cancelable_future_test.dart |
diff --git a/packages/analyzer/test/cancelable_future_test.dart b/packages/analyzer/test/cancelable_future_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b28c0f9482c1b99dcf41960b58bc9c4965ff6bec |
--- /dev/null |
+++ b/packages/analyzer/test/cancelable_future_test.dart |
@@ -0,0 +1,411 @@ |
+// 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'); |
+ }); |
+ } |
+} |