Chromium Code Reviews| Index: pkg/scheduled_test/lib/scheduled_test.dart |
| diff --git a/pkg/scheduled_test/lib/scheduled_test.dart b/pkg/scheduled_test/lib/scheduled_test.dart |
| index cf61c2599c9dfc64b11a3a0ff4e51a661deaca64..b2f01c59d16eb0889c6405efbf99d13fb38c47cd 100644 |
| --- a/pkg/scheduled_test/lib/scheduled_test.dart |
| +++ b/pkg/scheduled_test/lib/scheduled_test.dart |
| @@ -30,10 +30,22 @@ export 'src/task.dart'; |
| Schedule get currentSchedule => _currentSchedule; |
| Schedule _currentSchedule; |
| -/// The user-provided setUp function. This is set for each test during |
| -/// `unittest.setUp`. |
| +/// The user-provided set-up function for the currently-running test. |
| +/// |
| +/// This is set for each test during `unittest.setUp`. |
| Function _setUpFn; |
| +/// The user-provided tear-down function for the currently-running test. |
| +/// |
| +/// This is set for each test during `unittest.setUp`. |
|
Bob Nystrom
2014/06/27 17:04:53
tearDown.
nweiz
2014/06/30 21:10:20
It actually is set during unittest's setUp.
|
| +Function _tearDownFn; |
| + |
| +/// The user-provided set-up function for the current test scope. |
| +Function _setUpForGroup; |
| + |
| +/// The user-provided tear-down function for the current test scope. |
| +Function _tearDownForGroup; |
| + |
| /// Creates a new test case with the given description and body. |
| /// |
| /// This has the same semantics as [unittest.test]. |
| @@ -59,7 +71,7 @@ void _test(String description, body(), Function testFn) { |
| } |
| unittest.ensureInitialized(); |
| - _ensureSetUpForTopLevel(); |
| + _initializeForGroup(); |
| testFn(description, () { |
| var completer = new Completer(); |
| @@ -73,6 +85,7 @@ void _test(String description, body(), Function testFn) { |
| return currentSchedule.run(() { |
| if (_setUpFn != null) maybeWrapFuture(_setUpFn(), "set up"); |
| maybeWrapFuture(body(), "test body"); |
| + if (_tearDownFn != null) maybeWrapFuture(_tearDownFn(), "tear down"); |
| }).catchError((error, stackTrace) { |
| if (error is ScheduleError) { |
| assert(error.schedule.errors.contains(error)); |
| @@ -96,11 +109,20 @@ bool _inGroup = false; |
| /// [unittest.group]. |
| void group(String description, void body()) { |
| unittest.ensureInitialized(); |
| - _ensureSetUpForTopLevel(); |
| + _initializeForGroup(); |
| unittest.group(description, () { |
| + var oldSetUp = _setUpForGroup; |
| + var oldTearDown = _tearDownForGroup; |
| + var wasInitializedForGroup = _initializedForGroup; |
| var wasInGroup = _inGroup; |
| + _setUpForGroup = null; |
| + _tearDownForGroup = null; |
| + _initializedForGroup = false; |
| _inGroup = true; |
| body(); |
| + _setUpForGroup = oldSetUp; |
| + _tearDownForGroup = oldTearDown; |
| + _initializedForGroup = wasInitializedForGroup; |
|
Bob Nystrom
2014/06/27 17:04:53
Is it worth doing this in a finally block?
nweiz
2014/06/30 21:10:20
I really doubt anyone is going to be doing serious
|
| _inGroup = wasInGroup; |
| }); |
| } |
| @@ -124,71 +146,89 @@ void group(String description, void body()) { |
| Future schedule(fn(), [String description]) => |
| currentSchedule.tasks.schedule(fn, description); |
| -/// Register a [setUp] function for a test [group]. This has the same semantics |
| -/// as [unittest.setUp]. Tasks may be scheduled using [schedule] within |
| -/// [setUpFn], and [currentSchedule] may be accessed as well. |
| +/// Register a [setUp] function for a test [group]. |
| /// |
| -/// Note that there is no associated [tearDown] function. Instead, tasks should |
| -/// be scheduled for [currentSchedule.onComplete] or |
| -/// [currentSchedule.onException]. These tasks will be run after each test's |
| -/// schedule is completed. |
| +/// This has the same semantics as [unittest.setUp]. Tasks may be scheduled |
| +/// using [schedule] within [setUpFn], and [currentSchedule] may be accessed as |
| +/// well. |
| void setUp(setUpFn()) { |
| - _setUpScheduledTest(setUpFn); |
| + _setUpForGroup = setUpFn; |
| } |
| -/// Whether [unittest.setUp] has been called in the top level scope. |
| -bool _setUpForTopLevel = false; |
| - |
| -/// If we're in the top-level scope (that is, not in any [group]s) and |
| -/// [unittest.setUp] hasn't been called yet, call it. |
| -void _ensureSetUpForTopLevel() { |
| - if (_inGroup || _setUpForTopLevel) return; |
| - _setUpScheduledTest(); |
| +/// Register a [tearDown] function for a test [group]. |
| +/// |
| +/// This has the same semantics as [unittest.tearDown]. Tasks may be scheduled |
| +/// using [schedule] within [tearDownFn], and [currentSchedule] may be accessed |
| +/// as well. Note that [tearDownFn] will be run synchronously after the test |
| +/// body finishes running, which means it will run before any scheduled tasks |
| +/// have begun. |
|
Bob Nystrom
2014/06/27 17:04:53
Would be good to point the reader to docs for the
nweiz
2014/06/30 21:10:20
Done.
|
| +void tearDown(tearDownFn()) { |
| + _tearDownForGroup = tearDownFn; |
| } |
| +/// Whether [_initializeForGroup] has been called in this group scope. |
| +bool _initializedForGroup = false; |
| + |
| /// Registers callbacks for [unittest.setUp] and [unittest.tearDown] that set up |
| -/// and tear down the scheduled test infrastructure. |
| -void _setUpScheduledTest([void setUpFn()]) { |
| - if (!_inGroup) { |
| - _setUpForTopLevel = true; |
| - var oldWrapAsync = unittest.wrapAsync; |
| - unittest.setUp(() { |
| - if (currentSchedule != null) { |
| - throw new StateError('There seems to be another scheduled test ' |
| - 'still running.'); |
| - } |
| - |
| - unittest.wrapAsync = (f, [description]) { |
| - // It's possible that this setup is run before a vanilla unittest test |
| - // if [unittest.test] is run in the same context as |
| - // [scheduled_test.test]. In that case, [currentSchedule] will never be |
| - // set and we should forward to the [unittest.wrapAsync]. |
| - if (currentSchedule == null) return oldWrapAsync(f, description); |
| - return currentSchedule.wrapAsync(f, description); |
| - }; |
| - |
| - if (_setUpFn != null) { |
| - var parentFn = _setUpFn; |
| - _setUpFn = () { parentFn(); setUpFn(); }; |
| - } else { |
| - _setUpFn = setUpFn; |
| - } |
| - }); |
| - |
| - unittest.tearDown(() { |
| - unittest.wrapAsync = oldWrapAsync; |
| - _currentSchedule = null; |
| - _setUpFn = null; |
| - }); |
| - } else { |
| - unittest.setUp(() { |
| - if (_setUpFn != null) { |
| - var parentFn = _setUpFn; |
| - _setUpFn = () { parentFn(); setUpFn(); }; |
| - } else { |
| - _setUpFn = setUpFn; |
| - } |
| - }); |
| +/// and tear down the scheduled test infrastructure and run the user's [setUp] |
| +/// and [tearDown] callbacks. |
| +void _initializeForGroup() { |
| + if (_initializedForGroup) return; |
| + _initializedForGroup = true; |
| + |
| + var setUpFn = _setUpForGroup; |
| + var tearDownFn = _tearDownForGroup; |
| + |
| + if (_inGroup) { |
| + unittest.setUp(() => _addSetUpTearDown(setUpFn, tearDownFn)); |
| + return; |
| + } |
| + |
| + var oldWrapAsync = unittest.wrapAsync; |
| + unittest.setUp(() { |
| + if (currentSchedule != null) { |
| + throw new StateError('There seems to be another scheduled test ' |
| + 'still running.'); |
| + } |
| + |
| + unittest.wrapAsync = (f, [description]) { |
| + // It's possible that this setup is run before a vanilla unittest test |
| + // if [unittest.test] is run in the same context as |
| + // [scheduled_test.test]. In that case, [currentSchedule] will never be |
| + // set and we should forward to the [unittest.wrapAsync]. |
| + if (currentSchedule == null) return oldWrapAsync(f, description); |
| + return currentSchedule.wrapAsync(f, description); |
| + }; |
| + |
| + _addSetUpTearDown(setUpFn, tearDownFn); |
| + }); |
| + |
| + unittest.tearDown(() { |
| + unittest.wrapAsync = oldWrapAsync; |
| + _currentSchedule = null; |
| + _setUpFn = null; |
| + _tearDownFn = null; |
| + }); |
| +} |
| + |
| +/// Set [_setUpFn] and [_tearDownFn] appropriately. |
| +void _addSetUpTearDown(void setUpFn(), void tearDownFn()) { |
| + if (setUpFn != null) { |
| + if (_setUpFn != null) { |
| + var parentFn = _setUpFn; |
| + _setUpFn = () { parentFn(); setUpFn(); }; |
| + } else { |
| + _setUpFn = setUpFn; |
| + } |
| + } |
| + |
| + if (tearDownFn != null) { |
| + if (_tearDownFn != null) { |
| + var parentFn = _tearDownFn; |
| + _tearDownFn = () { parentFn(); tearDownFn(); }; |
| + } else { |
| + _tearDownFn = tearDownFn; |
| + } |
| } |
| } |