Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: lib/test_reflective_loader.dart

Issue 2388073004: Switch to 'test', add different support for 'solo'. (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« CHANGELOG.md ('K') | « CHANGELOG.md ('k') | pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library test_reflective_loader; 5 library test_reflective_loader;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 @MirrorsUsed(metaTargets: 'ReflectiveTest') 8 @MirrorsUsed(metaTargets: 'ReflectiveTest')
9 import 'dart:mirrors'; 9 import 'dart:mirrors';
10 10
11 import 'package:unittest/unittest.dart'; 11 import 'package:test/test.dart' as test_package;
12 12
13 /** 13 /**
14 * A marker annotation used to annotate overridden test methods (so we cannot 14 * A marker annotation used to annotate overridden test methods (so we cannot
15 * rename them to `fail_`) which are expected to fail at `assert` in the 15 * rename them to `fail_`) which are expected to fail at `assert` in the
16 * checked mode. 16 * checked mode.
17 */ 17 */
18 const _AssertFailingTest assertFailingTest = const _AssertFailingTest(); 18 const _AssertFailingTest assertFailingTest = const _AssertFailingTest();
19 19
20 /** 20 /**
21 * A marker annotation used to annotate overridden test methods (so we cannot 21 * A marker annotation used to annotate overridden test methods (so we cannot
22 * rename them to `fail_`) which are expected to fail. 22 * rename them to `fail_`) which are expected to fail.
23 */ 23 */
24 const _FailingTest failingTest = const _FailingTest(); 24 const _FailingTest failingTest = const _FailingTest();
25 25
26 /** 26 /**
27 * A marker annotation used to instruct dart2js to keep reflection information 27 * A marker annotation used to instruct dart2js to keep reflection information
28 * for the annotated classes. 28 * for the annotated classes.
29 */ 29 */
30 const ReflectiveTest reflectiveTest = const ReflectiveTest(); 30 const ReflectiveTest reflectiveTest = const ReflectiveTest();
31 31
32 /** 32 /**
33 * Test classes annotated with this annotation are run using [solo_group]. 33 * A marker annotation used to annotate "solo" groups and tests.
34 */ 34 */
35 const _SoloTest soloTest = const _SoloTest(); 35 const _SoloTest soloTest = const _SoloTest();
36 36
37 final List<_Group> _currentGroups = <_Group>[];
38 int _currentSuiteLevel = 0;
39 String _currentSuiteName = null;
40
37 /** 41 /**
38 * Is `true` the application is running in the checked mode. 42 * Is `true` the application is running in the checked mode.
39 */ 43 */
40 final bool _isCheckedMode = () { 44 final bool _isCheckedMode = () {
41 try { 45 try {
42 assert(false); 46 assert(false);
43 return false; 47 return false;
44 } catch (_) { 48 } catch (_) {
45 return true; 49 return true;
46 } 50 }
47 }(); 51 }();
48 52
49 /** 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 if (_currentSuiteLevel == 0) {
70 void runTests({bool allGroups, bool allTests}) {
71 for (_Group group in _currentGroups) {
72 if (allGroups || group.isSolo) {
73 for (_Test test in group.tests) {
74 if (allTests || test.isSolo) {
75 test_package.test(test.name, test.function);
76 }
77 }
78 }
79 }
80 }
81
82 if (_currentGroups.any((g) => g.hasSoloTest)) {
83 runTests(allGroups: true, allTests: false);
84 } else if (_currentGroups.any((g) => g.isSolo)) {
85 runTests(allGroups: false, allTests: true);
86 } else {
87 runTests(allGroups: true, allTests: true);
88 }
89 _currentGroups.clear();
90 }
91 }
92
93 /**
50 * Runs test methods existing in the given [type]. 94 * Runs test methods existing in the given [type].
51 * 95 *
52 * Methods with names starting with `test` are run using [test] function. 96 * 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.
53 * Methods with names starting with `solo_test` are run using [solo_test] functi on. 97 * methods are run.
98 *
99 * If there is a "solo" test type, only its test methods are run.
100 *
101 * Otherwise all tests methods of all test types are run.
54 * 102 *
55 * Each method is run with a new instance of [type]. 103 * Each method is run with a new instance of [type].
56 * So, [type] should have a default constructor. 104 * So, [type] should have a default constructor.
57 * 105 *
58 * If [type] declares method `setUp`, it methods will be invoked before any test 106 * If [type] declares method `setUp`, it methods will be invoked before any test
59 * method invocation. 107 * method invocation.
60 * 108 *
61 * If [type] declares method `tearDown`, it will be invoked after any test 109 * If [type] declares method `tearDown`, it will be invoked after any test
62 * method invocation. If method returns [Future] to test some asynchronous 110 * method invocation. If method returns [Future] to test some asynchronous
63 * behavior, then `tearDown` will be invoked in `Future.complete`. 111 * behavior, then `tearDown` will be invoked in `Future.complete`.
64 */ 112 */
65 void defineReflectiveTests(Type type) { 113 void defineReflectiveTests(Type type) {
66 ClassMirror classMirror = reflectClass(type); 114 ClassMirror classMirror = reflectClass(type);
67 if (!classMirror.metadata.any((InstanceMirror annotation) => 115 if (!classMirror.metadata.any((InstanceMirror annotation) =>
68 annotation.type.reflectedType == ReflectiveTest)) { 116 annotation.type.reflectedType == ReflectiveTest)) {
69 String name = MirrorSystem.getName(classMirror.qualifiedName); 117 String name = MirrorSystem.getName(classMirror.qualifiedName);
70 throw new Exception('Class $name must have annotation "@reflectiveTest" ' 118 throw new Exception('Class $name must have annotation "@reflectiveTest" '
71 'in order to be run by runReflectiveTests.'); 119 'in order to be run by runReflectiveTests.');
72 } 120 }
73 void runMembers() { 121
74 classMirror.instanceMembers 122 _Group group;
75 .forEach((Symbol symbol, MethodMirror memberMirror) { 123 {
76 // we need only methods 124 bool isSolo = _hasAnnotationInstance(classMirror, soloTest);
77 if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) { 125 String className = MirrorSystem.getName(classMirror.simpleName);
78 return; 126 group = new _Group(isSolo, _combineNames(_currentSuiteName, className));
79 } 127 _currentGroups.add(group);
80 String memberName = MirrorSystem.getName(symbol); 128 }
81 // test_ 129
82 if (memberName.startsWith('test_')) { 130 classMirror.instanceMembers
83 test(memberName, () { 131 .forEach((Symbol symbol, MethodMirror memberMirror) {
84 if (_hasFailingTestAnnotation(memberMirror) || 132 // we need only methods
85 _isCheckedMode && _hasAssertFailingTestAnnotation(memberMirror)) { 133 if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) {
86 return _runFailingTest(classMirror, symbol); 134 return;
87 } else { 135 }
88 return _runTest(classMirror, symbol); 136 // prepare information about the method
89 } 137 String memberName = MirrorSystem.getName(symbol);
90 }); 138 bool isSolo = memberName.startsWith('solo_') ||
91 return; 139 _hasAnnotationInstance(memberMirror, soloTest);
92 } 140 // test_
93 // solo_test_ 141 if (memberName.startsWith('test_')) {
94 if (memberName.startsWith('solo_test_')) { 142 group.addTest(isSolo, memberName, () {
95 solo_test(memberName, () { 143 if (_hasFailingTestAnnotation(memberMirror) ||
144 _isCheckedMode && _hasAssertFailingTestAnnotation(memberMirror)) {
145 return _runFailingTest(classMirror, symbol);
146 } else {
96 return _runTest(classMirror, symbol); 147 return _runTest(classMirror, symbol);
97 }); 148 }
98 } 149 });
99 // fail_test_ 150 return;
100 if (memberName.startsWith('fail_')) { 151 }
101 test(memberName, () { 152 // solo_test_
102 return _runFailingTest(classMirror, symbol); 153 if (memberName.startsWith('solo_test_')) {
103 }); 154 group.addTest(true, memberName, () {
104 } 155 return _runTest(classMirror, symbol);
105 // solo_fail_test_ 156 });
106 if (memberName.startsWith('solo_fail_')) { 157 }
107 solo_test(memberName, () { 158 // fail_test_
108 return _runFailingTest(classMirror, symbol); 159 if (memberName.startsWith('fail_')) {
109 }); 160 group.addTest(isSolo, memberName, () {
110 } 161 return _runFailingTest(classMirror, symbol);
111 }); 162 });
112 } 163 }
113 String className = MirrorSystem.getName(classMirror.simpleName); 164 // solo_fail_test_
114 if (_hasAnnotationInstance(classMirror, soloTest)) { 165 if (memberName.startsWith('solo_fail_')) {
115 solo_group(className, runMembers); 166 group.addTest(true, memberName, () {
167 return _runFailingTest(classMirror, symbol);
168 });
169 }
170 });
171 }
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.
172
173 /**
174 * Return the combination of the [base] and [addition] names.
175 * If any other two is `null`, then the other one is returned.
176 */
177 String _combineNames(String base, String addition) {
178 if (base == null) {
179 return addition;
180 } else if (addition == null) {
181 return base;
116 } else { 182 } else {
117 group(className, runMembers); 183 return '$base | $addition';
118 } 184 }
119 } 185 }
120 186
121 bool _hasAnnotationInstance(DeclarationMirror declaration, instance) => 187 bool _hasAnnotationInstance(DeclarationMirror declaration, instance) =>
122 declaration.metadata.any((InstanceMirror annotation) => 188 declaration.metadata.any((InstanceMirror annotation) =>
123 identical(annotation.reflectee, instance)); 189 identical(annotation.reflectee, instance));
124 190
125 bool _hasAssertFailingTestAnnotation(MethodMirror method) => 191 bool _hasAssertFailingTestAnnotation(MethodMirror method) =>
126 _hasAnnotationInstance(method, assertFailingTest); 192 _hasAnnotationInstance(method, assertFailingTest);
127 193
(...skipping 18 matching lines...) Expand all
146 * 212 *
147 * This properly handles the following cases: 213 * This properly handles the following cases:
148 * - The test fails by throwing an exception 214 * - The test fails by throwing an exception
149 * - The test returns a future which completes with an error. 215 * - The test returns a future which completes with an error.
150 * 216 *
151 * However, it does not handle the case where the test creates an asynchronous 217 * However, it does not handle the case where the test creates an asynchronous
152 * callback using expectAsync(), and that callback generates a failure. 218 * callback using expectAsync(), and that callback generates a failure.
153 */ 219 */
154 Future _runFailingTest(ClassMirror classMirror, Symbol symbol) { 220 Future _runFailingTest(ClassMirror classMirror, Symbol symbol) {
155 return new Future(() => _runTest(classMirror, symbol)).then((_) { 221 return new Future(() => _runTest(classMirror, symbol)).then((_) {
156 fail('Test passed - expected to fail.'); 222 test_package.fail('Test passed - expected to fail.');
157 }, onError: (_) {}); 223 }, onError: (_) {});
158 } 224 }
159 225
160 _runTest(ClassMirror classMirror, Symbol symbol) { 226 _runTest(ClassMirror classMirror, Symbol symbol) {
161 InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []); 227 InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []);
162 return _invokeSymbolIfExists(instanceMirror, #setUp) 228 return _invokeSymbolIfExists(instanceMirror, #setUp)
163 .then((_) => instanceMirror.invoke(symbol, []).reflectee) 229 .then((_) => instanceMirror.invoke(symbol, []).reflectee)
164 .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown)); 230 .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown));
165 } 231 }
166 232
233 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.
234
167 /** 235 /**
168 * A marker annotation used to instruct dart2js to keep reflection information 236 * A marker annotation used to instruct dart2js to keep reflection information
169 * for the annotated classes. 237 * for the annotated classes.
170 */ 238 */
171 class ReflectiveTest { 239 class ReflectiveTest {
172 const ReflectiveTest(); 240 const ReflectiveTest();
173 } 241 }
174 242
175 /** 243 /**
176 * A marker annotation used to annotate overridden test methods (so we cannot 244 * A marker annotation used to annotate overridden test methods (so we cannot
177 * rename them to `fail_`) which are expected to fail at `assert` in the 245 * rename them to `fail_`) which are expected to fail at `assert` in the
178 * checked mode. 246 * checked mode.
179 */ 247 */
180 class _AssertFailingTest { 248 class _AssertFailingTest {
181 const _AssertFailingTest(); 249 const _AssertFailingTest();
182 } 250 }
183 251
184 /** 252 /**
185 * A marker annotation used to annotate overridden test methods (so we cannot 253 * A marker annotation used to annotate overridden test methods (so we cannot
186 * rename them to `fail_`) which are expected to fail. 254 * rename them to `fail_`) which are expected to fail.
187 */ 255 */
188 class _FailingTest { 256 class _FailingTest {
189 const _FailingTest(); 257 const _FailingTest();
190 } 258 }
191 259
192 /** 260 /**
193 * A marker annotation used to annotate a test class to run it using 261 * Information about a type based test group.
194 * [solo_group]. 262 */
263 class _Group {
264 final bool isSolo;
265 final String name;
266 final List<_Test> tests = <_Test>[];
267
268 _Group(this.isSolo, this.name);
269
270 bool get hasSoloTest => tests.any((test) => test.isSolo);
271
272 void addTest(bool isSolo, String name, _TestFunction function) {
273 String fullName = _combineNames(this.name, name);
274 tests.add(new _Test(isSolo, fullName, function));
275 }
276 }
277
278 /**
279 * A marker annotation used to annotate "solo" groups and tests.
195 */ 280 */
196 class _SoloTest { 281 class _SoloTest {
197 const _SoloTest(); 282 const _SoloTest();
198 } 283 }
284
285 /**
286 * Information about a test.
287 */
288 class _Test {
289 final bool isSolo;
290 final String name;
291 final _TestFunction function;
292
293 _Test(this.isSolo, this.name, this.function);
294 }
OLDNEW
« CHANGELOG.md ('K') | « CHANGELOG.md ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698