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/pkgs/test_reflective_loader/CHANGELOG.md b/pkgs/test_reflective_loader/CHANGELOG.md index da6f2f7..49931bb 100644 --- a/pkgs/test_reflective_loader/CHANGELOG.md +++ b/pkgs/test_reflective_loader/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/pkgs/test_reflective_loader/lib/test_reflective_loader.dart b/pkgs/test_reflective_loader/lib/test_reflective_loader.dart index 6e3c945..375f584 100644 --- a/pkgs/test_reflective_loader/lib/test_reflective_loader.dart +++ b/pkgs/test_reflective_loader/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/pkgs/test_reflective_loader/pubspec.yaml b/pkgs/test_reflective_loader/pubspec.yaml index 8b93c1e..9373aec 100644 --- a/pkgs/test_reflective_loader/pubspec.yaml +++ b/pkgs/test_reflective_loader/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