blob: d1bc67a390a07ed39c45e04fc13001e6c7945b4a [file] [log] [blame]
// Copyright (c) 2015, 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 test.backend.group;
import 'dart:async';
import '../utils.dart';
import 'invoker.dart';
import 'metadata.dart';
/// A group contains multiple tests and subgroups.
///
/// A group has a description that is prepended to that of all nested tests and
/// subgroups. It also has [setUp] and [tearDown] functions which are scoped to
/// the tests and groups it contains.
class Group {
/// The parent group, or `null` if this is the root group.
final Group parent;
/// The description of the current test group, or `null` if this is the root
/// group.
final String _description;
/// The metadata for this group, including the metadata of any parent groups.
Metadata get metadata {
if (parent == null) return _metadata;
return parent.metadata.merge(_metadata);
}
final Metadata _metadata;
/// The set-up function for this group, or `null`.
AsyncFunction setUp;
/// The tear-down function for this group, or `null`.
AsyncFunction tearDown;
/// Returns the description for this group, including the description of any
/// parent groups.
///
/// If this is the root group, returns `null`.
String get description {
if (parent == null || parent.description == null) return _description;
return "${parent.description} $_description";
}
/// Creates a new root group.
///
/// This is the implicit group that exists outside of any calls to `group()`.
Group.root()
: this(null, null, new Metadata());
Group(this.parent, this._description, this._metadata);
/// 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 runSetUp() {
// TODO(nweiz): Use async/await here once issue 23497 has been fixed in two
// stable versions.
if (parent != null) {
return parent.runSetUp().then((_) {
if (setUp != null) return setUp();
});
}
if (setUp != null) return new Future.sync(setUp);
return new Future.value();
}
/// 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.
Future runTearDown() {
// TODO(nweiz): Use async/await here once issue 23497 has been fixed in two
// stable versions.
if (parent == null) {
return tearDown == null ? new Future.value() : new Future.sync(tearDown);
}
return _errorsDontStopTest(() {
if (tearDown != null) return tearDown();
}).then((_) => parent.runTearDown());
}
/// Runs [body] with special error-handling behavior.
///
/// Errors emitted [body] will still cause be the 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.waitForOutstandingCallbacks(() {
new Future.sync(body).whenComplete(completer.complete);
});
return completer.future;
}
}