Chromium Code Reviews| Index: lib/test_reflective_loader.dart |
| diff --git a/lib/test_reflective_loader.dart b/lib/test_reflective_loader.dart |
| index 6e3c945a2778a7b7ba82ae05733c300f0a81c533..b926170d88cbe5d886191e54c6d264696696643b 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,54 @@ 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--; |
| + } |
| + 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(); |
| + } |
| +} |
| + |
| +/** |
| * 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, it only "solo" |
|
Paul Berry
2016/10/04 19:19:12
Drop the word "it"
scheglov
2016/10/04 19:30:11
Done.
|
| + * 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 +113,74 @@ 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, () { |
| - 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); |
| + }); |
| + } |
| + }); |
| +} |
|
Paul Berry
2016/10/04 19:19:12
AFAICT, if defineReflectiveTests() is called witho
scheglov
2016/10/04 19:30:11
Good idea.
Done.
Thanks.
|
| + |
| +/** |
| + * 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 +219,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 +230,8 @@ _runTest(ClassMirror classMirror, Symbol symbol) { |
| .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown)); |
| } |
| +typedef void _TestFunction(); |
|
Paul Berry
2016/10/04 19:19:12
Return type needs to be `dynamic`, since some test
scheglov
2016/10/04 19:30:11
Done.
|
| + |
| /** |
| * A marker annotation used to instruct dart2js to keep reflection information |
| * for the annotated classes. |
| @@ -190,9 +258,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); |
| +} |