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

Side by Side Diff: lib/test_reflective_loader.dart

Issue 2388073004: Switch to 'test', add different support for 'solo'. (Closed)
Patch Set: Fixes for review comments. 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
« no previous file with comments | « 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 _addTestsIfTopLevelSuite();
70 }
71
72 /**
50 * Runs test methods existing in the given [type]. 73 * Runs test methods existing in the given [type].
51 * 74 *
52 * Methods with names starting with `test` are run using [test] function. 75 * If there is a "solo" test method in the top-level suite, only "solo" methods
53 * Methods with names starting with `solo_test` are run using [solo_test] functi on. 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.
54 * 81 *
55 * Each method is run with a new instance of [type]. 82 * Each method is run with a new instance of [type].
56 * So, [type] should have a default constructor. 83 * So, [type] should have a default constructor.
57 * 84 *
58 * If [type] declares method `setUp`, it methods will be invoked before any test 85 * If [type] declares method `setUp`, it methods will be invoked before any test
59 * method invocation. 86 * method invocation.
60 * 87 *
61 * If [type] declares method `tearDown`, it will be invoked after any test 88 * If [type] declares method `tearDown`, it will be invoked after any test
62 * method invocation. If method returns [Future] to test some asynchronous 89 * method invocation. If method returns [Future] to test some asynchronous
63 * behavior, then `tearDown` will be invoked in `Future.complete`. 90 * behavior, then `tearDown` will be invoked in `Future.complete`.
64 */ 91 */
65 void defineReflectiveTests(Type type) { 92 void defineReflectiveTests(Type type) {
66 ClassMirror classMirror = reflectClass(type); 93 ClassMirror classMirror = reflectClass(type);
67 if (!classMirror.metadata.any((InstanceMirror annotation) => 94 if (!classMirror.metadata.any((InstanceMirror annotation) =>
68 annotation.type.reflectedType == ReflectiveTest)) { 95 annotation.type.reflectedType == ReflectiveTest)) {
69 String name = MirrorSystem.getName(classMirror.qualifiedName); 96 String name = MirrorSystem.getName(classMirror.qualifiedName);
70 throw new Exception('Class $name must have annotation "@reflectiveTest" ' 97 throw new Exception('Class $name must have annotation "@reflectiveTest" '
71 'in order to be run by runReflectiveTests.'); 98 'in order to be run by runReflectiveTests.');
72 } 99 }
73 void runMembers() { 100
74 classMirror.instanceMembers 101 _Group group;
75 .forEach((Symbol symbol, MethodMirror memberMirror) { 102 {
76 // we need only methods 103 bool isSolo = _hasAnnotationInstance(classMirror, soloTest);
77 if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) { 104 String className = MirrorSystem.getName(classMirror.simpleName);
78 return; 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)) {
124 return _runFailingTest(classMirror, symbol);
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 }
79 } 169 }
80 String memberName = MirrorSystem.getName(symbol); 170 }
81 // test_ 171
82 if (memberName.startsWith('test_')) { 172 if (_currentGroups.any((g) => g.hasSoloTest)) {
83 test(memberName, () { 173 runTests(allGroups: true, allTests: false);
84 if (_hasFailingTestAnnotation(memberMirror) || 174 } else if (_currentGroups.any((g) => g.isSolo)) {
85 _isCheckedMode && _hasAssertFailingTestAnnotation(memberMirror)) { 175 runTests(allGroups: false, allTests: true);
86 return _runFailingTest(classMirror, symbol); 176 } else {
87 } else { 177 runTests(allGroups: true, allTests: true);
88 return _runTest(classMirror, symbol); 178 }
89 } 179 _currentGroups.clear();
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, () {
108 return _runFailingTest(classMirror, symbol);
109 });
110 }
111 });
112 }
113 String className = MirrorSystem.getName(classMirror.simpleName);
114 if (_hasAnnotationInstance(classMirror, soloTest)) {
115 solo_group(className, runMembers);
116 } else {
117 group(className, runMembers);
118 } 180 }
119 } 181 }
120 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;
192 } else {
193 return '$base | $addition';
194 }
195 }
196
121 bool _hasAnnotationInstance(DeclarationMirror declaration, instance) => 197 bool _hasAnnotationInstance(DeclarationMirror declaration, instance) =>
122 declaration.metadata.any((InstanceMirror annotation) => 198 declaration.metadata.any((InstanceMirror annotation) =>
123 identical(annotation.reflectee, instance)); 199 identical(annotation.reflectee, instance));
124 200
125 bool _hasAssertFailingTestAnnotation(MethodMirror method) => 201 bool _hasAssertFailingTestAnnotation(MethodMirror method) =>
126 _hasAnnotationInstance(method, assertFailingTest); 202 _hasAnnotationInstance(method, assertFailingTest);
127 203
128 bool _hasFailingTestAnnotation(MethodMirror method) => 204 bool _hasFailingTestAnnotation(MethodMirror method) =>
129 _hasAnnotationInstance(method, failingTest); 205 _hasAnnotationInstance(method, failingTest);
130 206
(...skipping 15 matching lines...) Expand all
146 * 222 *
147 * This properly handles the following cases: 223 * This properly handles the following cases:
148 * - The test fails by throwing an exception 224 * - The test fails by throwing an exception
149 * - The test returns a future which completes with an error. 225 * - The test returns a future which completes with an error.
150 * 226 *
151 * However, it does not handle the case where the test creates an asynchronous 227 * However, it does not handle the case where the test creates an asynchronous
152 * callback using expectAsync(), and that callback generates a failure. 228 * callback using expectAsync(), and that callback generates a failure.
153 */ 229 */
154 Future _runFailingTest(ClassMirror classMirror, Symbol symbol) { 230 Future _runFailingTest(ClassMirror classMirror, Symbol symbol) {
155 return new Future(() => _runTest(classMirror, symbol)).then((_) { 231 return new Future(() => _runTest(classMirror, symbol)).then((_) {
156 fail('Test passed - expected to fail.'); 232 test_package.fail('Test passed - expected to fail.');
157 }, onError: (_) {}); 233 }, onError: (_) {});
158 } 234 }
159 235
160 _runTest(ClassMirror classMirror, Symbol symbol) { 236 _runTest(ClassMirror classMirror, Symbol symbol) {
161 InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []); 237 InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []);
162 return _invokeSymbolIfExists(instanceMirror, #setUp) 238 return _invokeSymbolIfExists(instanceMirror, #setUp)
163 .then((_) => instanceMirror.invoke(symbol, []).reflectee) 239 .then((_) => instanceMirror.invoke(symbol, []).reflectee)
164 .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown)); 240 .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown));
165 } 241 }
166 242
243 typedef _TestFunction();
244
167 /** 245 /**
168 * A marker annotation used to instruct dart2js to keep reflection information 246 * A marker annotation used to instruct dart2js to keep reflection information
169 * for the annotated classes. 247 * for the annotated classes.
170 */ 248 */
171 class ReflectiveTest { 249 class ReflectiveTest {
172 const ReflectiveTest(); 250 const ReflectiveTest();
173 } 251 }
174 252
175 /** 253 /**
176 * A marker annotation used to annotate overridden test methods (so we cannot 254 * 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 255 * rename them to `fail_`) which are expected to fail at `assert` in the
178 * checked mode. 256 * checked mode.
179 */ 257 */
180 class _AssertFailingTest { 258 class _AssertFailingTest {
181 const _AssertFailingTest(); 259 const _AssertFailingTest();
182 } 260 }
183 261
184 /** 262 /**
185 * A marker annotation used to annotate overridden test methods (so we cannot 263 * A marker annotation used to annotate overridden test methods (so we cannot
186 * rename them to `fail_`) which are expected to fail. 264 * rename them to `fail_`) which are expected to fail.
187 */ 265 */
188 class _FailingTest { 266 class _FailingTest {
189 const _FailingTest(); 267 const _FailingTest();
190 } 268 }
191 269
192 /** 270 /**
193 * A marker annotation used to annotate a test class to run it using 271 * Information about a type based test group.
194 * [solo_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.
195 */ 290 */
196 class _SoloTest { 291 class _SoloTest {
197 const _SoloTest(); 292 const _SoloTest();
198 } 293 }
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 }
OLDNEW
« no previous file with comments | « CHANGELOG.md ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698