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

Unified Diff: lib/src/backend/declarer.dart

Issue 1379203002: Refactor groups to pipe them through to the runner. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 5 years, 2 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 | lib/src/backend/group.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/backend/declarer.dart
diff --git a/lib/src/backend/declarer.dart b/lib/src/backend/declarer.dart
index df2ec06ee47b600e9a1b9a5baad65a326091e43f..a3c201172575148630e887c3a9529e9dcc2e7961 100644
--- a/lib/src/backend/declarer.dart
+++ b/lib/src/backend/declarer.dart
@@ -4,41 +4,76 @@
library test.backend.declarer;
-import 'dart:collection';
+import 'dart:async';
import '../frontend/timeout.dart';
+import '../utils.dart';
import 'group.dart';
import 'invoker.dart';
import 'metadata.dart';
-import 'test.dart';
+import 'suite_entry.dart';
/// A class that manages the state of tests as they're declared.
///
-/// This is in charge of tracking the current group, set-up, and tear-down
-/// functions. It produces a list of runnable [tests].
+/// A nested tree of Declarers tracks the current group, set-up, and tear-down
+/// functions. Each Declarer in the tree corresponds to a group. This tree is
+/// tracked by a zone-scoped "current" Declarer; the current declarer can be set
+/// for a block using [Declarer.declare], and it can be accessed using
+/// [Declarer.current].
class Declarer {
- /// The current group.
- var _group = new Group.root();
+ /// The parent declarer, or `null` if this corresponds to the root group.
+ final Declarer _parent;
- /// The list of tests that have been defined.
- List<Test> get tests => new UnmodifiableListView<Test>(_tests);
- final _tests = new List<Test>();
+ /// The name of the current test group, including the name of any parent
+ /// groups.
+ ///
+ /// This is `null` if this is the root group.
+ final String _name;
- Declarer();
+ /// The metadata for this group, including the metadata of any parent groups
+ /// and of the test suite.
+ final Metadata _metadata;
- /// Defines a test case with the given description and body.
- void test(String description, body(), {String testOn, Timeout timeout,
- skip, Map<String, dynamic> onPlatform}) {
- // TODO(nweiz): Once tests have begun running, throw an error if [test] is
- // called.
- var prefix = _group.description;
- if (prefix != null) description = "$prefix $description";
+ /// The set-up functions for this group.
+ final _setUps = new List<AsyncFunction>();
- var metadata = _group.metadata.merge(new Metadata.parse(
+ /// The tear-down functions for this group.
+ final _tearDowns = new List<AsyncFunction>();
+
+ /// The children of this group, either tests or sub-groups.
+ final _entries = new List<SuiteEntry>();
+
+ /// Whether [build] has been called for this declarer.
+ bool _built = false;
+
+ /// The current zone-scoped declarer.
+ static Declarer get current => Zone.current[#test.declarer];
+
+ /// Creates a new declarer for the root group.
+ ///
+ /// This is the implicit group that exists outside of any calls to `group()`.
+ /// [metadata] should be the suite's metadata, if available.
+ Declarer([Metadata metadata])
+ : this._(null, null, metadata == null ? new Metadata() : metadata);
+
+ Declarer._(this._parent, this._name, this._metadata);
+
+ /// Runs [body] with this declarer as [Declarer.current].
+ ///
+ /// Returns the return value of [body].
+ declare(body()) => runZoned(body, zoneValues: {#test.declarer: this});
+
+ /// Defines a test case with the given name and body.
+ void test(String name, body(), {String testOn, Timeout timeout, skip,
+ Map<String, dynamic> onPlatform}) {
+ if (_built) {
+ throw new StateError("Can't call test() once tests have begun running.");
+ }
+
+ var metadata = _metadata.merge(new Metadata.parse(
testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform));
- var group = _group;
- _tests.add(new LocalTest(description, metadata, () {
+ _entries.add(new LocalTest(_prefix(name), metadata, () {
// TODO(nweiz): It might be useful to throw an error here if a test starts
// running while other tests from the same declarer are also running,
// since they might share closurized state.
@@ -46,40 +81,110 @@ class Declarer {
// TODO(nweiz): Use async/await here once issue 23497 has been fixed in
// two stable versions.
return Invoker.current.waitForOutstandingCallbacks(() {
- return group.runSetUps().then((_) => body());
- }).then((_) => group.runTearDowns());
+ return _runSetUps().then((_) => body());
+ }).then((_) => _runTearDowns());
}));
}
/// Creates a group of tests.
- void group(String description, void body(), {String testOn,
- Timeout timeout, skip, Map<String, dynamic> onPlatform}) {
- var oldGroup = _group;
+ void group(String name, void body(), {String testOn, Timeout timeout, skip,
+ Map<String, dynamic> onPlatform}) {
+ if (_built) {
+ throw new StateError("Can't call group() once tests have begun running.");
+ }
- var metadata = new Metadata.parse(
- testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform);
+ var metadata = _metadata.merge(new Metadata.parse(
+ testOn: testOn, timeout: timeout, skip: skip, onPlatform: onPlatform));
- // Don' load the tests for a skipped group.
+ // Don't load the tests for a skipped group.
if (metadata.skip) {
- _tests.add(new LocalTest(description, metadata, () {}));
+ _entries.add(new Group(name, metadata, []));
return;
}
- _group = new Group(oldGroup, description, metadata);
- try {
- body();
- } finally {
- _group = oldGroup;
- }
+ var declarer = new Declarer._(this, _prefix(name), metadata);
+ declarer.declare(body);
+ _entries.add(new Group(declarer._name, metadata, declarer.build()));
}
- /// Registers a function to be run before tests.
+ /// Returns [name] prefixed with this declarer's group name.
+ String _prefix(String name) => _name == null ? name : "$_name $name";
+
+ /// Registers a function to be run before each test in this group.
void setUp(callback()) {
- _group.setUps.add(callback);
+ if (_built) {
+ throw new StateError("Can't call setUp() once tests have begun running.");
+ }
+
+ _setUps.add(callback);
}
- /// Registers a function to be run after tests.
+ /// Registers a function to be run after each test in this group.
void tearDown(callback()) {
- _group.tearDowns.add(callback);
+ if (_built) {
+ throw new StateError(
+ "Can't call tearDown() once tests have begun running.");
+ }
+
+ _tearDowns.add(callback);
+ }
+
+ /// Finalizes and returns the tests and groups being declared.
+ List<SuiteEntry> build() {
+ if (_built) {
+ throw new StateError("Can't call Declarer.build() more than once.");
+ }
+
+ _built = true;
+ return _entries.toList();
+ }
+
+ /// Run the set-up functions for this and any parent groups.
+ ///
+ /// If no set-up functions are declared, this returns a [Future] that
+ /// completes immediately.
+ Future _runSetUps() {
+ // TODO(nweiz): Use async/await here once issue 23497 has been fixed in two
+ // stable versions.
+ if (_parent != null) {
+ return _parent._runSetUps().then((_) {
+ return Future.forEach(_setUps, (setUp) => setUp());
+ });
+ }
+
+ return Future.forEach(_setUps, (setUp) => setUp());
+ }
+
+ /// Run the tear-up functions for this and any parent groups.
+ ///
+ /// If no set-up functions are declared, this returns a [Future] that
+ /// completes immediately.
+ ///
+ /// This should only be called within a test.
+ Future _runTearDowns() {
+ return Invoker.current.unclosable(() {
+ var tearDowns = [];
+ for (var declarer = this; declarer != null; declarer = declarer._parent) {
+ tearDowns.addAll(declarer._tearDowns.reversed);
+ }
+
+ return Future.forEach(tearDowns, _errorsDontStopTest);
+ });
+ }
+
+ /// Runs [body] with special error-handling behavior.
+ ///
+ /// Errors emitted [body] will still cause the current test to fail, but they
+ /// won't cause it to *stop*. In particular, they won't remove any outstanding
+ /// callbacks registered outside of [body].
+ Future _errorsDontStopTest(body()) {
+ var completer = new Completer();
+
+ Invoker.current.addOutstandingCallback();
+ Invoker.current.waitForOutstandingCallbacks(() {
+ new Future.sync(body).whenComplete(completer.complete);
+ }).then((_) => Invoker.current.removeOutstandingCallback());
+
+ return completer.future;
}
}
« no previous file with comments | « no previous file | lib/src/backend/group.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698