cb4a19b5fc
Change-Id: I42a5c41394226f6ddc9c0608c6d688ae683ffdea Reviewed-on: https://dart-review.googlesource.com/62680 Commit-Queue: Kevin Moore <kevmoo@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
428 lines
13 KiB
Dart
428 lines
13 KiB
Dart
// 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<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 TypeMatcher<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 TypeMatcher<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 TypeMatcher<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 TypeMatcher<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();
|
|
}, 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 TypeMatcher<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');
|
|
});
|
|
}
|
|
}
|