OLD | NEW |
| (Empty) |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
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. | |
4 | |
5 library analyzer.test.reflective_tests; | |
6 | |
7 import 'dart:async'; | |
8 @MirrorsUsed(metaTargets: 'ReflectiveTest') | |
9 import 'dart:mirrors'; | |
10 | |
11 import 'package:unittest/unittest.dart'; | |
12 | |
13 /** | |
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 | |
16 * checked mode. | |
17 */ | |
18 const _AssertFailingTest assertFailingTest = const _AssertFailingTest(); | |
19 | |
20 /** | |
21 * A marker annotation used to annotate overridden test methods (so we cannot | |
22 * rename them to `fail_`) which are expected to fail. | |
23 */ | |
24 const _FailingTest failingTest = const _FailingTest(); | |
25 | |
26 /** | |
27 * A marker annotation used to instruct dart2js to keep reflection information | |
28 * for the annotated classes. | |
29 */ | |
30 const ReflectiveTest reflectiveTest = const ReflectiveTest(); | |
31 | |
32 /** | |
33 * Test classes annotated with this annotation are run using [solo_group]. | |
34 */ | |
35 const _SoloTest soloTest = const _SoloTest(); | |
36 | |
37 /** | |
38 * Is `true` the application is running in the checked mode. | |
39 */ | |
40 final bool _isCheckedMode = () { | |
41 try { | |
42 assert(false); | |
43 return false; | |
44 } catch (_) { | |
45 return true; | |
46 } | |
47 }(); | |
48 | |
49 /** | |
50 * Runs test methods existing in the given [type]. | |
51 * | |
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] functi
on. | |
54 * | |
55 * Each method is run with a new instance of [type]. | |
56 * So, [type] should have a default constructor. | |
57 * | |
58 * If [type] declares method `setUp`, it methods will be invoked before any test | |
59 * method invocation. | |
60 * | |
61 * If [type] declares method `tearDown`, it will be invoked after any test | |
62 * method invocation. If method returns [Future] to test some asynchronous | |
63 * behavior, then `tearDown` will be invoked in `Future.complete`. | |
64 */ | |
65 void runReflectiveTests(Type type) { | |
66 ClassMirror classMirror = reflectClass(type); | |
67 if (!classMirror.metadata.any((InstanceMirror annotation) => | |
68 annotation.type.reflectedType == ReflectiveTest)) { | |
69 String name = MirrorSystem.getName(classMirror.qualifiedName); | |
70 throw new Exception('Class $name must have annotation "@reflectiveTest" ' | |
71 'in order to be run by runReflectiveTests.'); | |
72 } | |
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, () { | |
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 } | |
119 } | |
120 | |
121 bool _hasAnnotationInstance(DeclarationMirror declaration, instance) => | |
122 declaration.metadata.any((InstanceMirror annotation) => | |
123 identical(annotation.reflectee, instance)); | |
124 | |
125 bool _hasAssertFailingTestAnnotation(MethodMirror method) => | |
126 _hasAnnotationInstance(method, assertFailingTest); | |
127 | |
128 bool _hasFailingTestAnnotation(MethodMirror method) => | |
129 _hasAnnotationInstance(method, failingTest); | |
130 | |
131 Future _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) { | |
132 var invocationResult = null; | |
133 InstanceMirror closure; | |
134 try { | |
135 closure = instanceMirror.getField(symbol); | |
136 } on NoSuchMethodError {} | |
137 | |
138 if (closure is ClosureMirror) { | |
139 invocationResult = closure.apply([]).reflectee; | |
140 } | |
141 return new Future.value(invocationResult); | |
142 } | |
143 | |
144 /** | |
145 * Run a test that is expected to fail, and confirm that it fails. | |
146 * | |
147 * This properly handles the following cases: | |
148 * - The test fails by throwing an exception | |
149 * - The test returns a future which completes with an error. | |
150 * | |
151 * However, it does not handle the case where the test creates an asynchronous | |
152 * callback using expectAsync(), and that callback generates a failure. | |
153 */ | |
154 Future _runFailingTest(ClassMirror classMirror, Symbol symbol) { | |
155 return new Future(() => _runTest(classMirror, symbol)).then((_) { | |
156 fail('Test passed - expected to fail.'); | |
157 }, onError: (_) {}); | |
158 } | |
159 | |
160 _runTest(ClassMirror classMirror, Symbol symbol) { | |
161 InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []); | |
162 return _invokeSymbolIfExists(instanceMirror, #setUp) | |
163 .then((_) => instanceMirror.invoke(symbol, []).reflectee) | |
164 .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown)); | |
165 } | |
166 | |
167 /** | |
168 * A marker annotation used to instruct dart2js to keep reflection information | |
169 * for the annotated classes. | |
170 */ | |
171 class ReflectiveTest { | |
172 const ReflectiveTest(); | |
173 } | |
174 | |
175 /** | |
176 * 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 | |
178 * checked mode. | |
179 */ | |
180 class _AssertFailingTest { | |
181 const _AssertFailingTest(); | |
182 } | |
183 | |
184 /** | |
185 * A marker annotation used to annotate overridden test methods (so we cannot | |
186 * rename them to `fail_`) which are expected to fail. | |
187 */ | |
188 class _FailingTest { | |
189 const _FailingTest(); | |
190 } | |
191 | |
192 /** | |
193 * A marker annotation used to annotate a test class to run it using | |
194 * [solo_group]. | |
195 */ | |
196 class _SoloTest { | |
197 const _SoloTest(); | |
198 } | |
OLD | NEW |