@@ -8,7 +8,7 @@ import 'dart:async';
88@MirrorsUsed (metaTargets: 'ReflectiveTest' )
99import 'dart:mirrors' ;
1010
11- import 'package:unittest/unittest .dart' ;
11+ import 'package:test/test .dart' as test_package ;
1212
1313/**
1414 * A marker annotation used to annotate overridden test methods (so we cannot
@@ -30,10 +30,14 @@ const _FailingTest failingTest = const _FailingTest();
3030const ReflectiveTest reflectiveTest = const ReflectiveTest ();
3131
3232/**
33- * Test classes annotated with this annotation are run using [solo_group] .
33+ * A marker annotation used to annotate "solo" groups and tests .
3434 */
3535const _SoloTest soloTest = const _SoloTest ();
3636
37+ final List <_Group > _currentGroups = < _Group > [];
38+ int _currentSuiteLevel = 0 ;
39+ String _currentSuiteName = null ;
40+
3741/**
3842 * Is `true` the application is running in the checked mode.
3943 */
@@ -46,11 +50,34 @@ final bool _isCheckedMode = () {
4650 }
4751}();
4852
53+ /**
54+ * Run the [define] function parameter that calls [defineReflectiveTests] to
55+ * add normal and "solo" tests, and also calls [defineReflectiveSuite] to
56+ * create embedded suites. If the current suite is the top-level one, perform
57+ * check for "solo" groups and tests, and run all or only "solo" items.
58+ */
59+ void defineReflectiveSuite (void define (), {String name}) {
60+ String groupName = _currentSuiteName;
61+ _currentSuiteLevel++ ;
62+ try {
63+ _currentSuiteName = _combineNames (_currentSuiteName, name);
64+ define ();
65+ } finally {
66+ _currentSuiteName = groupName;
67+ _currentSuiteLevel-- ;
68+ }
69+ _addTestsIfTopLevelSuite ();
70+ }
71+
4972/**
5073 * Runs test methods existing in the given [type] .
5174 *
52- * Methods with names starting with `test` are run using [test] function.
53- * Methods with names starting with `solo_test` are run using [solo_test] function.
75+ * If there is a "solo" test method in the top-level suite, only "solo" methods
76+ * are run.
77+ *
78+ * If there is a "solo" test type, only its test methods are run.
79+ *
80+ * Otherwise all tests methods of all test types are run.
5481 *
5582 * Each method is run with a new instance of [type] .
5683 * So, [type] should have a default constructor.
@@ -65,56 +92,105 @@ final bool _isCheckedMode = () {
6592void defineReflectiveTests (Type type) {
6693 ClassMirror classMirror = reflectClass (type);
6794 if (! classMirror.metadata.any ((InstanceMirror annotation) =>
68- annotation.type.reflectedType == ReflectiveTest )) {
95+ annotation.type.reflectedType == ReflectiveTest )) {
6996 String name = MirrorSystem .getName (classMirror.qualifiedName);
7097 throw new Exception ('Class $name must have annotation "@reflectiveTest" '
7198 'in order to be run by runReflectiveTests.' );
7299 }
73- void runMembers () {
74- classMirror.instanceMembers
75- .forEach ((Symbol symbol, MethodMirror memberMirror) {
76- // we need only methods
77- if (memberMirror is ! MethodMirror || ! memberMirror.isRegularMethod) {
78- return ;
79- }
80- String memberName = MirrorSystem .getName (symbol);
81- // test_
82- if (memberName.startsWith ('test_' )) {
83- test (memberName, () {
84- if (_hasFailingTestAnnotation (memberMirror) ||
85- _isCheckedMode && _hasAssertFailingTestAnnotation (memberMirror)) {
86- return _runFailingTest (classMirror, symbol);
87- } else {
88- return _runTest (classMirror, symbol);
89- }
90- });
91- return ;
92- }
93- // solo_test_
94- if (memberName.startsWith ('solo_test_' )) {
95- solo_test (memberName, () {
96- return _runTest (classMirror, symbol);
97- });
98- }
99- // fail_test_
100- if (memberName.startsWith ('fail_' )) {
101- test (memberName, () {
102- return _runFailingTest (classMirror, symbol);
103- });
104- }
105- // solo_fail_test_
106- if (memberName.startsWith ('solo_fail_' )) {
107- solo_test (memberName, () {
100+
101+ _Group group;
102+ {
103+ bool isSolo = _hasAnnotationInstance (classMirror, soloTest);
104+ String className = MirrorSystem .getName (classMirror.simpleName);
105+ group = new _Group (isSolo, _combineNames (_currentSuiteName, className));
106+ _currentGroups.add (group);
107+ }
108+
109+ classMirror.instanceMembers
110+ .forEach ((Symbol symbol, MethodMirror memberMirror) {
111+ // we need only methods
112+ if (memberMirror is ! MethodMirror || ! memberMirror.isRegularMethod) {
113+ return ;
114+ }
115+ // prepare information about the method
116+ String memberName = MirrorSystem .getName (symbol);
117+ bool isSolo = memberName.startsWith ('solo_' ) ||
118+ _hasAnnotationInstance (memberMirror, soloTest);
119+ // test_
120+ if (memberName.startsWith ('test_' )) {
121+ group.addTest (isSolo, memberName, () {
122+ if (_hasFailingTestAnnotation (memberMirror) ||
123+ _isCheckedMode && _hasAssertFailingTestAnnotation (memberMirror)) {
108124 return _runFailingTest (classMirror, symbol);
109- });
125+ } else {
126+ return _runTest (classMirror, symbol);
127+ }
128+ });
129+ return ;
130+ }
131+ // solo_test_
132+ if (memberName.startsWith ('solo_test_' )) {
133+ group.addTest (true , memberName, () {
134+ return _runTest (classMirror, symbol);
135+ });
136+ }
137+ // fail_test_
138+ if (memberName.startsWith ('fail_' )) {
139+ group.addTest (isSolo, memberName, () {
140+ return _runFailingTest (classMirror, symbol);
141+ });
142+ }
143+ // solo_fail_test_
144+ if (memberName.startsWith ('solo_fail_' )) {
145+ group.addTest (true , memberName, () {
146+ return _runFailingTest (classMirror, symbol);
147+ });
148+ }
149+ });
150+
151+ // Support for the case of missing enclosing [defineReflectiveSuite].
152+ _addTestsIfTopLevelSuite ();
153+ }
154+
155+ /**
156+ * If the current suite is the top-level one, add tests to the `test` package.
157+ */
158+ void _addTestsIfTopLevelSuite () {
159+ if (_currentSuiteLevel == 0 ) {
160+ void runTests ({bool allGroups, bool allTests}) {
161+ for (_Group group in _currentGroups) {
162+ if (allGroups || group.isSolo) {
163+ for (_Test test in group.tests) {
164+ if (allTests || test.isSolo) {
165+ test_package.test (test.name, test.function);
166+ }
167+ }
168+ }
110169 }
111- });
170+ }
171+
172+ if (_currentGroups.any ((g) => g.hasSoloTest)) {
173+ runTests (allGroups: true , allTests: false );
174+ } else if (_currentGroups.any ((g) => g.isSolo)) {
175+ runTests (allGroups: false , allTests: true );
176+ } else {
177+ runTests (allGroups: true , allTests: true );
178+ }
179+ _currentGroups.clear ();
112180 }
113- String className = MirrorSystem .getName (classMirror.simpleName);
114- if (_hasAnnotationInstance (classMirror, soloTest)) {
115- solo_group (className, runMembers);
181+ }
182+
183+ /**
184+ * Return the combination of the [base] and [addition] names.
185+ * If any other two is `null` , then the other one is returned.
186+ */
187+ String _combineNames (String base , String addition) {
188+ if (base == null ) {
189+ return addition;
190+ } else if (addition == null ) {
191+ return base ;
116192 } else {
117- group (className, runMembers) ;
193+ return '$ base | $ addition ' ;
118194 }
119195}
120196
@@ -153,7 +229,7 @@ Future _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) {
153229 */
154230Future _runFailingTest (ClassMirror classMirror, Symbol symbol) {
155231 return new Future (() => _runTest (classMirror, symbol)).then ((_) {
156- fail ('Test passed - expected to fail.' );
232+ test_package. fail ('Test passed - expected to fail.' );
157233 }, onError: (_) {});
158234}
159235
@@ -164,6 +240,8 @@ _runTest(ClassMirror classMirror, Symbol symbol) {
164240 .whenComplete (() => _invokeSymbolIfExists (instanceMirror, #tearDown));
165241}
166242
243+ typedef _TestFunction ();
244+
167245/**
168246 * A marker annotation used to instruct dart2js to keep reflection information
169247 * for the annotated classes.
@@ -190,9 +268,37 @@ class _FailingTest {
190268}
191269
192270/**
193- * A marker annotation used to annotate a test class to run it using
194- * [solo_group] .
271+ * Information about a type based test group.
272+ */
273+ class _Group {
274+ final bool isSolo;
275+ final String name;
276+ final List <_Test > tests = < _Test > [];
277+
278+ _Group (this .isSolo, this .name);
279+
280+ bool get hasSoloTest => tests.any ((test) => test.isSolo);
281+
282+ void addTest (bool isSolo, String name, _TestFunction function) {
283+ String fullName = _combineNames (this .name, name);
284+ tests.add (new _Test (isSolo, fullName, function));
285+ }
286+ }
287+
288+ /**
289+ * A marker annotation used to annotate "solo" groups and tests.
195290 */
196291class _SoloTest {
197292 const _SoloTest ();
198293}
294+
295+ /**
296+ * Information about a test.
297+ */
298+ class _Test {
299+ final bool isSolo;
300+ final String name;
301+ final _TestFunction function;
302+
303+ _Test (this .isSolo, this .name, this .function);
304+ }
0 commit comments