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

Unified Diff: lib/unittest/unittest.dart

Issue 10037027: unittest step2: bye bye to multiple entrypoints for unittest (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 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
Index: lib/unittest/unittest.dart
diff --git a/lib/unittest/shared.dart b/lib/unittest/unittest.dart
similarity index 52%
rename from lib/unittest/shared.dart
rename to lib/unittest/unittest.dart
index 879e4da362de7a6e90db33a16e504ea7b49c95f6..1db7cec37d9a3568a839618ae7b3d09b57649a5c 100644
--- a/lib/unittest/shared.dart
+++ b/lib/unittest/unittest.dart
@@ -3,14 +3,108 @@
// BSD-style license that can be found in the LICENSE file.
/**
- * This file is sourced from unittest_html, unittest_dom, and unittest_vm.
- * These libraries shold also source 'config.dart' and should define a class
- * called [PlatformConfiguration] that implements [Configuration].
+ * A library for writing dart unit tests.
+ *
+ * **Concepts**
Bob Nystrom 2012/04/12 16:45:49 Use Markdown, one of: ## Concepts ## Concepts ##
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Done.
+ *
+ * * Tests: Tests are specified via the top-level function [test], they can be
+ * organized together using [group].
+ * * Checks: Test expectations can be specified via [expect] (see methods in
+ * [Expectation]), [expectThrows], or using assertions with the [Expect]
+ * class.
+ * * Configuration: The framework can be adapted by calling [configure] with a
+ * configuration. Common configurations can be found in this package under:
+ * 'dom\_config.dart', 'html\_config.dart', and 'vm\_config.dart'.
+ *
+ * **Examples**
+ *
+ * A trivial test:
+ * #import('path-to-dart/lib/unittest/unitest.dart');
Bob Nystrom 2012/04/12 16:45:49 Need blank line above this to separate out code bl
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Ok, it didn't seem to need it when I created the p
Bob Nystrom 2012/04/12 18:01:37 Hmm, yeah I think dartdoc will allow it but most m
+ * main() {
+ * test('this is a test', () {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * });
+ * }
+ *
+ * Multiple tests:
+ * #import('path-to-dart/lib/unittest/unitest.dart');
+ * main() {
+ * test('this is a test', () {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * });
+ * test('this is another test', () {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * });
+ * }
+ *
+ * Multiple tests, grouped by category:
+ * #import('path-to-dart/lib/unittest/unitest.dart');
+ * main() {
+ * group('group A', () {
+ * test('test A.1', () {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * });
+ * test('test A.2', () {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * });
+ * });
+ * group('group B', () {
+ * test('this B.1', () {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * });
+ * });
+ * }
+ *
+ * Asynchronous tests: under the current API (soon to be deprecated):
+ * #import('path-to-dart/lib/unittest/unitest.dart');
+ * #import('dart:dom');
+ * main() {
+ * // use [asyncTest], indicate the expected number of callbacks:
+ * asyncTest('this is a test', 1, () {
+ * window.setTimeout(() {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * // invoke [callbackDone] at the end of the callback.
+ * callbackDone();
+ * }, 0);
+ * });
+ * }
+ *
+ * We plan to replace this with a different API, one API we are considering is:
+ * #import('path-to-dart/lib/unittest/unitest.dart');
+ * #import('dart:dom');
+ * main() {
+ * test('this is a test', () {
+ * // wrap the callback of an asynchronous call with [later]
+ * window.setTimeout(later(() {
+ * int x = 2 + 3;
+ * expect(x).equals(5);
+ * }), 0);
+ * });
+ * }
Bob Nystrom 2012/04/12 16:45:49 This documentation is excellent!
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Thanks!
Emily Fortuna 2012/04/12 17:48:42 +1
*/
+#library('unittest');
+
+#import('dart:isolate');
+
+#source('config.dart');
+#source('expectation.dart');
+#source('testcase.dart');
Bob Nystrom 2012/04/12 16:45:49 "test_case"
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Done.
/** [Configuration] used by the unittest library. */
Configuration _config = null;
+/** Set the [Configuration] used by the unittest library. */
+void configure(Configuration c) {
Bob Nystrom 2012/04/12 16:45:49 "c" -> "config".
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Done.
+ _config = c;
+}
+
/**
* Description text of the current test group. If multiple groups are nested,
* this will contain all of their text concatenated.
@@ -79,6 +173,7 @@ void test(String spec, TestFunction body) {
* description will include the descriptions of any surrounding group()
* calls.
*/
+// TODO(sigmund): deprecate this API
void asyncTest(String spec, int callbacks, TestFunction body) {
_ensureInitialized();
@@ -92,6 +187,81 @@ void asyncTest(String spec, int callbacks, TestFunction body) {
}
}
+class _SentinelType {
Bob Nystrom 2012/04/12 16:45:49 I'd just do "_Sentinel".
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Done.
+ const _SentinelType();
+}
+
+final _SentinelType _sentinel = const _SentinelType();
Bob Nystrom 2012/04/12 16:45:49 Move this into the class and rename, like: class
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 cool, done
Emily Fortuna 2012/04/12 17:48:42 +1
+
+/**
+ * Indicate to the unittest framework that a callback is expected. [callback]
+ * can take any number of arguments between 0 and 4.
+ *
+ * The framework will wait for the callback to run before it continues with the
+ * following test. The callback must excute once and only once. Using [later]
+ * will also ensure that errors that occur within the callback are tracked and
+ * reported by the unittest framework.
+ */
+// TODO(sigmund): expose this functionality
+Function _later(Function callback) {
Emily Fortuna 2012/04/12 17:48:42 Perhaps I missed it, but where is this function ca
Siggi Cherem (dart-lang) 2012/04/12 21:13:36 it is not yet (see note below about step 3 :-))
+ Expect.isTrue(_currentTest < _tests.length);
+ var testCase = _tests[_currentTest];
+ testCase.callbacks++;
+ // We simulate spread arguments using named arguments:
+ // Note: this works in the vm and dart2js, but not in frog.
+ return ([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel,
+ arg3 = _sentinel, arg4 = _sentinel]) {
+ _guard(testCase, () {
+ if (arg0 == _sentinel) {
+ callback();
+ } else if (arg1 == _sentinel) {
+ callback(arg0);
+ } else if (arg3 == _sentinel) {
Emily Fortuna 2012/04/12 17:48:42 arg2
Siggi Cherem (dart-lang) 2012/04/12 21:13:36 nice catch
+ callback(arg0, arg1);
+ } else if (arg3 == _sentinel) {
+ callback(arg0, arg1, arg2);
+ } else if (arg4 == _sentinel) {
+ callback(arg0, arg1, arg2, arg3);
+ } else {
+ testCase.error(
+ 'unittest lib does not support callbacks with more than 4 arguments',
+ '');
+ _state = _UNCAUGHT_ERROR;
+ }
+ }, callbackDone);
+ };
+}
+
+// TODO(sigmund): expose this functionality
+Function _later0(Function callback) {
+ Expect.isTrue(_currentTest < _tests.length);
+ var testCase = _tests[_currentTest];
+ testCase.callbacks++;
+ return () {
+ _guard(testCase, () => callback(), callbackDone);
+ };
+}
+
+// TODO(sigmund): expose this functionality
+Function _later1(Function callback) {
+ Expect.isTrue(_currentTest < _tests.length);
+ var testCase = _tests[_currentTest];
+ testCase.callbacks++;
+ return (arg0) {
+ _guard(testCase, () => callback(arg0), callbackDone);
+ };
+}
+
+// TODO(sigmund): expose this functionality
+Function _later2(Function callback) {
Siggi Cherem (dart-lang) 2012/04/12 01:13:53 this is something I'll need for "step 3" (see emai
Bob Nystrom 2012/04/12 16:45:49 Nope, it's fine. Thanks for clarifying.
+ Expect.isTrue(_currentTest < _tests.length);
+ var testCase = _tests[_currentTest];
+ testCase.callbacks++;
+ return (arg0, arg1) {
+ _guard(testCase, () => callback(arg0, arg1), callbackDone);
+ };
+}
+
/**
* Creates a new named group of tests. Calls to group() or test() within the
* body of the function passed to this will inherit this group's description.
@@ -121,11 +291,7 @@ void group(String description, void body()) {
void callbackDone() {
_callbacksCalled++;
final testCase = _tests[_currentTest];
- if (testCase.callbacks == 0) {
- testCase.error(
- "Can't call callbackDone() on a synchronous test", '');
- _state = _UNCAUGHT_ERROR;
- } else if (_callbacksCalled > testCase.callbacks) {
+ if (_callbacksCalled > testCase.callbacks) {
final expected = testCase.callbacks;
testCase.error(
'More calls to callbackDone() than expected. '
@@ -133,12 +299,24 @@ void callbackDone() {
_state = _UNCAUGHT_ERROR;
} else if ((_callbacksCalled == testCase.callbacks) &&
(_state != _RUNNING_TEST)) {
- testCase.pass();
+ if (testCase.result == null) testCase.pass();
_currentTest++;
_testRunner();
}
}
+void notifyError(String msg, String trace) {
+ if (_currentTest < _tests.length) {
+ final testCase = _tests[_currentTest];
+ testCase.error(msg, trace);
+ _state = _UNCAUGHT_ERROR;
+ if (testCase.callbacks > 0) {
+ _currentTest++;
+ _testRunner();
+ }
+ }
+}
+
/** Runs [callback] at the end of the event loop. */
_defer(void callback()) {
// Exploit isolate ports as a platform-independent mechanism to queue a
@@ -164,7 +342,7 @@ _runTests() {
/** Runs a single test. */
_runTest(TestCase testCase) {
Bob Nystrom 2012/04/12 16:45:49 I don't see this being called anywhere, can it be
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 It's actually called by [nextBatch] below. I decid
- try {
+ _guard(testCase, () {
_callbacksCalled = 0;
_state = _RUNNING_TEST;
@@ -175,7 +353,12 @@ _runTest(TestCase testCase) {
testCase.pass();
}
}
+ });
+}
+_guard(TestCase testCase, tryBody, [finallyBody]) {
Bob Nystrom 2012/04/12 16:45:49 Nice. In every callsite I see, testCase is always
Siggi Cherem (dart-lang) 2012/04/12 17:36:28 Done.
+ try {
+ tryBody();
} catch (ExpectException e, var trace) {
if (_state != _UNCAUGHT_ERROR) {
//TODO(pquitslund) remove guard once dartc reliably propagates traces
@@ -188,6 +371,7 @@ _runTest(TestCase testCase) {
}
} finally {
_state = _READY;
+ if (finallyBody != null) finallyBody();
}
}
@@ -246,9 +430,7 @@ _ensureInitialized() {
_testRunner = _nextBatch;
if (_config == null) {
- // TODO(sigmund): make this [new Configuration], set configuration
- // for each platform in test.dart
- _config = new PlatformConfiguration();
+ _config = new Configuration();
}
_config.onInit();
@@ -257,122 +439,5 @@ _ensureInitialized() {
_defer(_runTests);
}
-/**
- * Wraps an value and provides an "==" operator that can be used to verify that
- * the value matches a given expectation.
- */
-class Expectation {
- final _value;
-
- Expectation(this._value);
-
- /** Asserts that the value is equivalent to [expected]. */
- void equals(expected) {
- // Use the type-specialized versions when appropriate to give better
- // error messages.
- if (_value is String && expected is String) {
- Expect.stringEquals(expected, _value);
- } else if (_value is Map && expected is Map) {
- Expect.mapEquals(expected, _value);
- } else if (_value is Set && expected is Set) {
- Expect.setEquals(expected, _value);
- } else {
- Expect.equals(expected, _value);
- }
- }
-
- /**
- * Asserts that the difference between [expected] and the value is within
- * [tolerance]. If no tolerance is given, it is assumed to be the value 4
- * significant digits smaller than the expected value.
- */
- void approxEquals(num expected,
- [num tolerance = null, String reason = null]) {
- Expect.approxEquals(expected, _value, tolerance: tolerance, reason: reason);
- }
-
- /** Asserts that the value is [null]. */
- void isNull() {
- Expect.equals(null, _value);
- }
-
- /** Asserts that the value is not [null]. */
- void isNotNull() {
- Expect.notEquals(null, _value);
- }
-
- /** Asserts that the value is [true]. */
- void isTrue() {
- Expect.equals(true, _value);
- }
-
- /** Asserts that the value is [false]. */
- void isFalse() {
- Expect.equals(false, _value);
- }
-
- /** Asserts that the value has the same elements as [expected]. */
- void equalsCollection(Collection expected) {
- Expect.listEquals(expected, _value);
- }
-
- /**
- * Checks that every element of [expected] is also in [actual], and that
- * every element of [actual] is also in [expected].
- */
- void equalsSet(Iterable expected) {
- Expect.setEquals(expected, _value);
- }
-}
-
-/** Summarizes information about a single test case. */
-class TestCase {
- /** Identifier for this test. */
- final id;
-
- /** A description of what the test is specifying. */
- final String description;
-
- /** The body of the test case. */
- final TestFunction test;
-
- /** Total number of callbacks to wait for before the test completes. */
- int callbacks;
-
- /** Error or failure message. */
- String message = '';
-
- /**
- * One of [_PASS], [_FAIL], or [_ERROR] or [null] if the test hasn't run yet.
- */
- String result;
-
- /** Stack trace associated with this test, or null if it succeeded. */
- String stackTrace;
-
- Date startTime;
-
- Duration runningTime;
-
- TestCase(this.id, this.description, this.test, this.callbacks);
-
- bool get isComplete() => result != null;
-
- void pass() {
- result = _PASS;
- }
-
- void fail(String message_, String stackTrace_) {
- result = _FAIL;
- this.message = message_;
- this.stackTrace = stackTrace_;
- }
-
- void error(String message_, String stackTrace_) {
- result = _ERROR;
- this.message = message_;
- this.stackTrace = stackTrace_;
- }
-}
-
+/** Signature for a test function. */
typedef void TestFunction();

Powered by Google App Engine
This is Rietveld 408576698