Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(861)

Unified Diff: pkg/unittest/lib/unittest.dart

Issue 14189009: Revert r21707 "unittest: big cleanup, tightened test semantics" (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: only partial revert Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/unittest/lib/unittest.dart
diff --git a/pkg/unittest/lib/unittest.dart b/pkg/unittest/lib/unittest.dart
index c71d9ddc45e40aa9a8d3b41806d27251b773a48d..50d308b8d26c0d27b8b81441d1db82c5b0f4b929 100644
--- a/pkg/unittest/lib/unittest.dart
+++ b/pkg/unittest/lib/unittest.dart
@@ -211,10 +211,13 @@ Function _testSetup;
/** Teardown function called after each test in a group */
Function _testTeardown;
-TestCase _currentTestCase = null;
+int _currentTestCaseIndex = 0;
/** [TestCase] currently being executed. */
-TestCase get currentTestCase => _currentTestCase;
+TestCase get currentTestCase =>
+ (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < testCases.length)
+ ? testCases[_currentTestCaseIndex]
+ : null;
/** Whether the framework is in an initialized state. */
bool _initialized = false;
@@ -289,9 +292,10 @@ class _SpreadArgsHelper {
final int minExpectedCalls;
final int maxExpectedCalls;
final Function isDone;
- final TestCase testCase;
+ final int testNum;
final String id;
int actualCalls = 0;
+ TestCase testCase;
bool complete;
static const sentinel = const _Sentinel();
@@ -303,13 +307,19 @@ class _SpreadArgsHelper {
? minExpected
: maxExpected,
this.isDone = isDone,
- this.testCase = currentTestCase,
+ testNum = _currentTestCaseIndex,
this.id = _makeCallbackId(id, callback) {
- if(testCase == null) {
- throw new StateError("No valid test, did you forget to run your test "
- "inside a call to test()?");
+ ensureInitialized();
+ if (!(_currentTestCaseIndex >= 0 &&
+ _currentTestCaseIndex < testCases.length &&
+ testCases[_currentTestCaseIndex] != null)) {
+ print("No valid test, did you forget to run your test inside a call "
+ "to test()?");
}
-
+ assert(_currentTestCaseIndex >= 0 &&
+ _currentTestCaseIndex < testCases.length &&
+ testCases[_currentTestCaseIndex] != null);
+ testCase = testCases[_currentTestCaseIndex];
if (isDone != null || minExpected > 0) {
testCase._callbackFunctionsOutstanding++;
complete = false;
@@ -393,7 +403,7 @@ class _SpreadArgsHelper {
'');
}
},
- after, testCase);
+ after, testNum);
}
invoke0() {
@@ -403,7 +413,7 @@ class _SpreadArgsHelper {
return callback();
}
},
- after, testCase);
+ after, testNum);
}
invoke1(arg1) {
@@ -413,7 +423,7 @@ class _SpreadArgsHelper {
return callback(arg1);
}
},
- after, testCase);
+ after, testNum);
}
invoke2(arg1, arg2) {
@@ -423,7 +433,7 @@ class _SpreadArgsHelper {
return callback(arg1, arg2);
}
},
- after, testCase);
+ after, testNum);
}
}
@@ -431,6 +441,21 @@ class _SpreadArgsHelper {
* Indicate that [callback] is expected to be called a [count] number of times
* (by default 1). The unittest framework will wait for the callback to run the
* specified [count] times before it continues with the following test. Using
+ * [_expectAsync] will also ensure that errors that occur within [callback] are
+ * tracked and reported. [callback] should take between 0 and 4 positional
+ * arguments (named arguments are not supported here). [id] can be used
+ * to provide more descriptive error messages if the callback is called more
+ * often than expected.
+ */
+Function _expectAsync(Function callback,
+ {int count: 1, int max: 0, String id}) {
+ return new _SpreadArgsHelper(callback, count, max, null, id).invoke;
+}
+
+/**
+ * Indicate that [callback] is expected to be called a [count] number of times
+ * (by default 1). The unittest framework will wait for the callback to run the
+ * specified [count] times before it continues with the following test. Using
* [expectAsync0] will also ensure that errors that occur within [callback] are
* tracked and reported. [callback] should take 0 positional arguments (named
* arguments are not supported). [id] can be used to provide more
@@ -464,6 +489,20 @@ Function expectAsync2(Function callback,
/**
* Indicate that [callback] is expected to be called until [isDone] returns
+ * true. The unittest framework checks [isDone] after each callback and only
+ * when it returns true will it continue with the following test. Using
+ * [expectAsyncUntil] will also ensure that errors that occur within
+ * [callback] are tracked and reported. [callback] should take between 0 and
+ * 4 positional arguments (named arguments are not supported). [id] can be
+ * used to identify the callback in error messages (for example if it is called
+ * after the test case is complete).
+ */
+Function _expectAsyncUntil(Function callback, Function isDone, {String id}) {
+ return new _SpreadArgsHelper(callback, 0, -1, isDone, id).invoke;
+}
+
+/**
+ * Indicate that [callback] is expected to be called until [isDone] returns
* true. The unittest framework check [isDone] after each callback and only
* when it returns true will it continue with the following test. Using
* [expectAsyncUntil0] will also ensure that errors that occur within
@@ -498,6 +537,19 @@ Function expectAsyncUntil2(Function callback, Function isDone, {String id}) {
* function will be able to handle exceptions by directing them to the correct
* test. This is thus similar to expectAsync0. Use it to wrap any callbacks that
* might optionally be called but may never be called during the test.
+ * [callback] should take between 0 and 4 positional arguments (named arguments
+ * are not supported). [id] can be used to identify the callback in error
+ * messages (for example if it is called after the test case is complete).
+ */
+Function _protectAsync(Function callback, {String id}) {
+ return new _SpreadArgsHelper(callback, 0, -1, null, id).invoke;
+}
+
+/**
+ * Wraps the [callback] in a new function and returns that function. The new
+ * function will be able to handle exceptions by directing them to the correct
+ * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that
+ * might optionally be called but may never be called during the test.
* [callback] should take 0 positional arguments (named arguments are not
* supported). [id] can be used to identify the callback in error
* messages (for example if it is called after the test case is complete).
@@ -568,7 +620,6 @@ void group(String description, void body()) {
* case it must return a [Future].
*/
void setUp(Function setupTest) {
- _requireNotRunning();
_testSetup = setupTest;
}
@@ -581,24 +632,46 @@ void setUp(Function setupTest) {
* case it must return a [Future].
*/
void tearDown(Function teardownTest) {
- _requireNotRunning();
_testTeardown = teardownTest;
}
+/** Advance to the next test case. */
+void _nextTestCase() {
+ _defer(() {
+ _currentTestCaseIndex++;
+ _nextBatch();
+ });
+}
+
/**
* Utility function that can be used to notify the test framework that an
* error was caught outside of this library.
*/
void _reportTestError(String msg, String trace) {
- if (currentTestCase != null) {
- currentTestCase.error(msg, trace);
+ if (_currentTestCaseIndex < testCases.length) {
+ final testCase = testCases[_currentTestCaseIndex];
+ testCase.error(msg, trace);
} else {
_uncaughtErrorMessage = "$msg: $trace";
}
}
+/**
+ * Runs [callback] at the end of the event loop. Note that we don't wrap
+ * the callback in guardAsync; this is for test framework functions which
+ * should not be throwing unexpected exceptions that end up failing test
+ * cases! Furthermore, we need the final exception to be thrown but not
+ * caught by the test framework if any test cases failed. However, tests
+ * that make use of a similar defer function *should* wrap the callback
+ * (as we do in unitttest_test.dart).
+ */
+_defer(void callback()) {
+ (new Future.value()).then((_) => callback());
+}
+
void rerunTests() {
- assert(_uncaughtErrorMessage == null);
+ _uncaughtErrorMessage = null;
+ _initialized = true; // We don't want to reset the test array.
runTests();
}
@@ -608,8 +681,7 @@ void rerunTests() {
* in that it removes the tests completely.
*/
void filterTests(testFilter) {
- _requireNotRunning();
- Function filterFunction;
+ var filterFunction;
if (testFilter is String) {
RegExp re = new RegExp(testFilter);
filterFunction = (t) => re.hasMatch(t.description);
@@ -624,8 +696,7 @@ void filterTests(testFilter) {
/** Runs all queued tests, one at a time. */
void runTests() {
_ensureInitialized(false);
- assert(_currentTestCase == null);
-
+ _currentTestCaseIndex = 0;
_currentGroup = '';
// If we are soloing a test, remove all the others.
@@ -635,7 +706,9 @@ void runTests() {
_config.onStart();
- new Future(_nextBatch);
+ _defer(() {
+ _nextBatch();
+ });
}
/**
@@ -645,15 +718,15 @@ void runTests() {
* The value returned by [tryBody] (if any) is returned by [guardAsync].
*/
guardAsync(Function tryBody) {
- return _guardAsync(tryBody, null, currentTestCase);
+ return _guardAsync(tryBody, null, _currentTestCaseIndex);
}
-_guardAsync(Function tryBody, Function finallyBody, TestCase testCase) {
- assert(testCase != null);
+_guardAsync(Function tryBody, Function finallyBody, int testNum) {
+ assert(testNum >= 0);
try {
return tryBody();
} catch (e, trace) {
- _registerException(testCase, e, trace);
+ _registerException(testNum, e, trace);
} finally {
if (finallyBody != null) finallyBody();
}
@@ -663,50 +736,48 @@ _guardAsync(Function tryBody, Function finallyBody, TestCase testCase) {
* Registers that an exception was caught for the current test.
*/
void registerException(e, [trace]) {
- _registerException(currentTestCase, e, trace);
+ _registerException(_currentTestCaseIndex, e, trace);
}
/**
* Registers that an exception was caught for the current test.
*/
-void _registerException(TestCase testCase, e, [trace]) {
- assert(testCase != null);
+void _registerException(testNum, e, [trace]) {
trace = trace == null ? '' : trace.toString();
String message = (e is TestFailure) ? e.message : 'Caught $e';
- if (testCase.result == null) {
- testCase.fail(message, trace);
+ if (testCases[testNum].result == null) {
+ testCases[testNum].fail(message, trace);
} else {
- testCase.error(message, trace);
+ testCases[testNum].error(message, trace);
}
}
/**
- * Executes tests in order starting with the provided index.
- * If a test is synchronous, the following test is executed immediately
- * For asynchronous tests, a Future is returned which completes with _nextBatch
- * starting at the next index.
- * Future.forEach is explicitly not used because it slows down synchronous tests
- * noticeably
+ * Runs a batch of tests, yielding whenever an asynchronous test starts
+ * running. Tests will resume executing when such asynchronous test calls
+ * [done] or if it fails with an exception.
*/
-Future _nextBatch([int index = 0]) {
- for(int i = index; i < testCases.length; i++) {
- _currentTestCase = testCases[i];
-
- Future f = guardAsync(_currentTestCase._run);
-
- if(f != null) {
- return f.then((_) => _nextBatch(i + 1));
+void _nextBatch() {
+ while (true) {
+ if (_currentTestCaseIndex >= testCases.length) {
+ _completeTests();
+ break;
}
+ final testCase = testCases[_currentTestCaseIndex];
+ var f = _guardAsync(testCase._run, null, _currentTestCaseIndex);
+ if (f != null) {
+ f.whenComplete(() {
+ _nextTestCase(); // Schedule the next test.
+ });
+ break;
+ }
+ _currentTestCaseIndex++;
}
- _currentTestCase = null;
- return new Future(_completeTests);
}
/** Publish results on the page and notify controller. */
void _completeTests() {
- assert(_initialized);
- assert(_currentTestCase == null);
-
+ if (!_initialized) return;
int passed = 0;
int failed = 0;
int errors = 0;
@@ -721,8 +792,7 @@ void _completeTests() {
_config.onSummary(passed, failed, errors, testCases, _uncaughtErrorMessage);
_config.onDone(passed > 0 && failed == 0 && errors == 0 &&
_uncaughtErrorMessage == null);
-
- _uncaughtErrorMessage = null;
+ _initialized = false;
}
String _fullSpec(String spec) {
@@ -737,15 +807,7 @@ void ensureInitialized() {
_ensureInitialized(true);
}
-void _requireNotRunning() {
- if(_currentTestCase != null) {
- throw new StateError("A forbidden operation occured "
- "while tests were running");
- }
-}
-
void _ensureInitialized(bool configAutoStart) {
- _requireNotRunning();
if (_initialized) {
return;
}
@@ -763,7 +825,7 @@ void _ensureInitialized(bool configAutoStart) {
if (configAutoStart && _config.autoStart) {
// Immediately queue the suite up. It will run after a timeout (i.e. after
// main() has returned).
- new Future(runTests);
+ _defer(runTests);
}
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698