Switch to 'test', add different support for 'solo'.
R=brianwilkerson@google.com, paulberry@google.com
BUG=
Review URL: https://codereview.chromium.org/2388073004 .
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da6f2f7..49931bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## 0.1.0
+
+- Switched from 'package:unittest' to 'package:test'.
+- Since 'package:test' does not define 'solo_test', in order to keep
+ this functionality, `defineReflectiveSuite` must be used to wrap
+ all `defineReflectiveTests` invocations.
+
## 0.0.4
- Added @failingTest, @assertFailingTest and @soloTest annotations.
diff --git a/lib/test_reflective_loader.dart b/lib/test_reflective_loader.dart
index 6e3c945..375f584 100644
--- a/lib/test_reflective_loader.dart
+++ b/lib/test_reflective_loader.dart
@@ -8,7 +8,7 @@
@MirrorsUsed(metaTargets: 'ReflectiveTest')
import 'dart:mirrors';
-import 'package:unittest/unittest.dart';
+import 'package:test/test.dart' as test_package;
/**
* A marker annotation used to annotate overridden test methods (so we cannot
@@ -30,10 +30,14 @@
const ReflectiveTest reflectiveTest = const ReflectiveTest();
/**
- * Test classes annotated with this annotation are run using [solo_group].
+ * A marker annotation used to annotate "solo" groups and tests.
*/
const _SoloTest soloTest = const _SoloTest();
+final List<_Group> _currentGroups = <_Group>[];
+int _currentSuiteLevel = 0;
+String _currentSuiteName = null;
+
/**
* Is `true` the application is running in the checked mode.
*/
@@ -47,10 +51,33 @@
}();
/**
+ * Run the [define] function parameter that calls [defineReflectiveTests] to
+ * add normal and "solo" tests, and also calls [defineReflectiveSuite] to
+ * create embedded suites. If the current suite is the top-level one, perform
+ * check for "solo" groups and tests, and run all or only "solo" items.
+ */
+void defineReflectiveSuite(void define(), {String name}) {
+ String groupName = _currentSuiteName;
+ _currentSuiteLevel++;
+ try {
+ _currentSuiteName = _combineNames(_currentSuiteName, name);
+ define();
+ } finally {
+ _currentSuiteName = groupName;
+ _currentSuiteLevel--;
+ }
+ _addTestsIfTopLevelSuite();
+}
+
+/**
* Runs test methods existing in the given [type].
*
- * Methods with names starting with `test` are run using [test] function.
- * Methods with names starting with `solo_test` are run using [solo_test] function.
+ * If there is a "solo" test method in the top-level suite, only "solo" methods
+ * are run.
+ *
+ * If there is a "solo" test type, only its test methods are run.
+ *
+ * Otherwise all tests methods of all test types are run.
*
* Each method is run with a new instance of [type].
* So, [type] should have a default constructor.
@@ -65,56 +92,105 @@
void defineReflectiveTests(Type type) {
ClassMirror classMirror = reflectClass(type);
if (!classMirror.metadata.any((InstanceMirror annotation) =>
- annotation.type.reflectedType == ReflectiveTest)) {
+ annotation.type.reflectedType == ReflectiveTest)) {
String name = MirrorSystem.getName(classMirror.qualifiedName);
throw new Exception('Class $name must have annotation "@reflectiveTest" '
'in order to be run by runReflectiveTests.');
}
- void runMembers() {
- classMirror.instanceMembers
- .forEach((Symbol symbol, MethodMirror memberMirror) {
- // we need only methods
- if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) {
- return;
- }
- String memberName = MirrorSystem.getName(symbol);
- // test_
- if (memberName.startsWith('test_')) {
- test(memberName, () {
- if (_hasFailingTestAnnotation(memberMirror) ||
- _isCheckedMode && _hasAssertFailingTestAnnotation(memberMirror)) {
- return _runFailingTest(classMirror, symbol);
- } else {
- return _runTest(classMirror, symbol);
- }
- });
- return;
- }
- // solo_test_
- if (memberName.startsWith('solo_test_')) {
- solo_test(memberName, () {
- return _runTest(classMirror, symbol);
- });
- }
- // fail_test_
- if (memberName.startsWith('fail_')) {
- test(memberName, () {
- return _runFailingTest(classMirror, symbol);
- });
- }
- // solo_fail_test_
- if (memberName.startsWith('solo_fail_')) {
- solo_test(memberName, () {
- return _runFailingTest(classMirror, symbol);
- });
- }
- });
+
+ _Group group;
+ {
+ bool isSolo = _hasAnnotationInstance(classMirror, soloTest);
+ String className = MirrorSystem.getName(classMirror.simpleName);
+ group = new _Group(isSolo, _combineNames(_currentSuiteName, className));
+ _currentGroups.add(group);
}
- String className = MirrorSystem.getName(classMirror.simpleName);
- if (_hasAnnotationInstance(classMirror, soloTest)) {
- solo_group(className, runMembers);
+
+ classMirror.instanceMembers
+ .forEach((Symbol symbol, MethodMirror memberMirror) {
+ // we need only methods
+ if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) {
+ return;
+ }
+ // prepare information about the method
+ String memberName = MirrorSystem.getName(symbol);
+ bool isSolo = memberName.startsWith('solo_') ||
+ _hasAnnotationInstance(memberMirror, soloTest);
+ // test_
+ if (memberName.startsWith('test_')) {
+ group.addTest(isSolo, memberName, () {
+ if (_hasFailingTestAnnotation(memberMirror) ||
+ _isCheckedMode && _hasAssertFailingTestAnnotation(memberMirror)) {
+ return _runFailingTest(classMirror, symbol);
+ } else {
+ return _runTest(classMirror, symbol);
+ }
+ });
+ return;
+ }
+ // solo_test_
+ if (memberName.startsWith('solo_test_')) {
+ group.addTest(true, memberName, () {
+ return _runTest(classMirror, symbol);
+ });
+ }
+ // fail_test_
+ if (memberName.startsWith('fail_')) {
+ group.addTest(isSolo, memberName, () {
+ return _runFailingTest(classMirror, symbol);
+ });
+ }
+ // solo_fail_test_
+ if (memberName.startsWith('solo_fail_')) {
+ group.addTest(true, memberName, () {
+ return _runFailingTest(classMirror, symbol);
+ });
+ }
+ });
+
+ // Support for the case of missing enclosing [defineReflectiveSuite].
+ _addTestsIfTopLevelSuite();
+}
+
+/**
+ * If the current suite is the top-level one, add tests to the `test` package.
+ */
+void _addTestsIfTopLevelSuite() {
+ if (_currentSuiteLevel == 0) {
+ void runTests({bool allGroups, bool allTests}) {
+ for (_Group group in _currentGroups) {
+ if (allGroups || group.isSolo) {
+ for (_Test test in group.tests) {
+ if (allTests || test.isSolo) {
+ test_package.test(test.name, test.function);
+ }
+ }
+ }
+ }
+ }
+
+ if (_currentGroups.any((g) => g.hasSoloTest)) {
+ runTests(allGroups: true, allTests: false);
+ } else if (_currentGroups.any((g) => g.isSolo)) {
+ runTests(allGroups: false, allTests: true);
+ } else {
+ runTests(allGroups: true, allTests: true);
+ }
+ _currentGroups.clear();
+ }
+}
+
+/**
+ * Return the combination of the [base] and [addition] names.
+ * If any other two is `null`, then the other one is returned.
+ */
+String _combineNames(String base, String addition) {
+ if (base == null) {
+ return addition;
+ } else if (addition == null) {
+ return base;
} else {
- group(className, runMembers);
+ return '$base | $addition';
}
}
@@ -153,7 +229,7 @@
*/
Future _runFailingTest(ClassMirror classMirror, Symbol symbol) {
return new Future(() => _runTest(classMirror, symbol)).then((_) {
- fail('Test passed - expected to fail.');
+ test_package.fail('Test passed - expected to fail.');
}, onError: (_) {});
}
@@ -164,6 +240,8 @@
.whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown));
}
+typedef _TestFunction();
+
/**
* A marker annotation used to instruct dart2js to keep reflection information
* for the annotated classes.
@@ -190,9 +268,37 @@
}
/**
- * A marker annotation used to annotate a test class to run it using
- * [solo_group].
+ * Information about a type based test group.
+ */
+class _Group {
+ final bool isSolo;
+ final String name;
+ final List<_Test> tests = <_Test>[];
+
+ _Group(this.isSolo, this.name);
+
+ bool get hasSoloTest => tests.any((test) => test.isSolo);
+
+ void addTest(bool isSolo, String name, _TestFunction function) {
+ String fullName = _combineNames(this.name, name);
+ tests.add(new _Test(isSolo, fullName, function));
+ }
+}
+
+/**
+ * A marker annotation used to annotate "solo" groups and tests.
*/
class _SoloTest {
const _SoloTest();
}
+
+/**
+ * Information about a test.
+ */
+class _Test {
+ final bool isSolo;
+ final String name;
+ final _TestFunction function;
+
+ _Test(this.isSolo, this.name, this.function);
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 8b93c1e..9373aec 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: test_reflective_loader
-version: 0.0.4
+version: 0.1.0
description: Support for discovering tests and test suites using reflection.
author: Dart Team <misc@dartlang.org>
homepage: https://github.com/dart-lang/test_reflective_loader
@@ -8,4 +8,4 @@
sdk: '>=1.0.0 <2.0.0'
dev_dependencies:
- unittest: '>=0.9.0 <0.12.0'
+ test: ^0.12.0