| Index: lib/test_reflective_loader.dart
|
| diff --git a/lib/test_reflective_loader.dart b/lib/test_reflective_loader.dart
|
| index 6e3c945a2778a7b7ba82ae05733c300f0a81c533..375f5843c770dc822914de08b583f31a9df5d9d4 100644
|
| --- a/lib/test_reflective_loader.dart
|
| +++ b/lib/test_reflective_loader.dart
|
| @@ -8,7 +8,7 @@ import 'dart:async';
|
| @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 _FailingTest failingTest = const _FailingTest();
|
| 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 @@ final bool _isCheckedMode = () {
|
| }();
|
|
|
| /**
|
| + * 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 @@ final bool _isCheckedMode = () {
|
| 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, () {
|
| +
|
| + _Group group;
|
| + {
|
| + bool isSolo = _hasAnnotationInstance(classMirror, soloTest);
|
| + String className = MirrorSystem.getName(classMirror.simpleName);
|
| + group = new _Group(isSolo, _combineNames(_currentSuiteName, className));
|
| + _currentGroups.add(group);
|
| + }
|
| +
|
| + 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();
|
| }
|
| - String className = MirrorSystem.getName(classMirror.simpleName);
|
| - if (_hasAnnotationInstance(classMirror, soloTest)) {
|
| - solo_group(className, runMembers);
|
| +}
|
| +
|
| +/**
|
| + * 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 _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) {
|
| */
|
| 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 @@ _runTest(ClassMirror classMirror, Symbol symbol) {
|
| .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 @@ class _FailingTest {
|
| }
|
|
|
| /**
|
| - * 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);
|
| +}
|
|
|