// 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 analyzer.test.cancelable_future_test; import 'dart:async'; import 'package:analyzer/src/cancelable_future.dart'; import 'package:test/test.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; void main() { defineReflectiveSuite(() { defineReflectiveTests(CancelableCompleterTests); defineReflectiveTests(CancelableFutureTests); }); } @reflectiveTest class CancelableCompleterTests { CancelableCompleter completer; int cancelCount = 0; void setUp() { completer = new CancelableCompleter(() { 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()); // 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()); 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()); 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()); // 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(); }, throwsStateError); expect(() { completer.completeError(new Object()); }, throwsStateError); } 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(); }, throwsStateError); expect(() { completer.completeError(new Object()); }, throwsStateError); // 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()); // 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'); }); } }