OLD | NEW |
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 @MirrorsUsed(metaTargets: 'ReflectiveTest') | 8 @MirrorsUsed(metaTargets: 'ReflectiveTest') |
8 import 'dart:mirrors'; | 9 import 'dart:mirrors'; |
9 import 'dart:async'; | |
10 | 10 |
11 import 'package:unittest/unittest.dart'; | 11 import 'package:unittest/unittest.dart'; |
12 | 12 |
13 /** | 13 /** |
14 * Define tests using methods existing in the given [type]. | 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]. |
15 * | 51 * |
16 * Methods with names starting with `test` are run using [test] function. | 52 * Methods with names starting with `test` are run using [test] function. |
17 * Methods with names starting with `solo_test` are run using [solo_test] functi
on. | 53 * Methods with names starting with `solo_test` are run using [solo_test] functi
on. |
18 * | 54 * |
19 * Each method is run with a new instance of [type]. | 55 * Each method is run with a new instance of [type]. |
20 * So, [type] should have a default constructor. | 56 * So, [type] should have a default constructor. |
21 * | 57 * |
22 * If [type] declares method `setUp`, it methods will be invoked before any test | 58 * If [type] declares method `setUp`, it methods will be invoked before any test |
23 * method invocation. | 59 * method invocation. |
24 * | 60 * |
25 * If [type] declares method `tearDown`, it will be invoked after any test | 61 * If [type] declares method `tearDown`, it will be invoked after any test |
26 * method invocation. If method returns [Future] to test some asyncronous | 62 * method invocation. If method returns [Future] to test some asynchronous |
27 * behavior, then `tearDown` will be invoked in `Future.complete`. | 63 * behavior, then `tearDown` will be invoked in `Future.complete`. |
28 */ | 64 */ |
29 void defineReflectiveTests(Type type) { | 65 void defineReflectiveTests(Type type) { |
30 ClassMirror classMirror = reflectClass(type); | 66 ClassMirror classMirror = reflectClass(type); |
31 if (!classMirror.metadata.any((InstanceMirror annotation) => | 67 if (!classMirror.metadata.any((InstanceMirror annotation) => |
32 annotation.type.reflectedType == ReflectiveTest)) { | 68 annotation.type.reflectedType == ReflectiveTest)) { |
33 String name = MirrorSystem.getName(classMirror.qualifiedName); | 69 String name = MirrorSystem.getName(classMirror.qualifiedName); |
34 throw new Exception('Class $name must have annotation "@reflectiveTest" ' | 70 throw new Exception('Class $name must have annotation "@reflectiveTest" ' |
35 'in order to be run by runReflectiveTests.'); | 71 'in order to be run by runReflectiveTests.'); |
36 } | 72 } |
37 String className = MirrorSystem.getName(classMirror.simpleName); | 73 void runMembers() { |
38 group(className, () { | 74 classMirror.instanceMembers |
39 classMirror.instanceMembers.forEach((symbol, memberMirror) { | 75 .forEach((Symbol symbol, MethodMirror memberMirror) { |
40 // we need only methods | 76 // we need only methods |
41 if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) { | 77 if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) { |
42 return; | 78 return; |
43 } | 79 } |
44 String memberName = MirrorSystem.getName(symbol); | 80 String memberName = MirrorSystem.getName(symbol); |
45 // test_ | 81 // test_ |
46 if (memberName.startsWith('test_')) { | 82 if (memberName.startsWith('test_')) { |
47 test(memberName, () { | 83 test(memberName, () { |
48 return _runTest(classMirror, symbol); | 84 if (_hasFailingTestAnnotation(memberMirror) || |
| 85 _isCheckedMode && _hasAssertFailingTestAnnotation(memberMirror)) { |
| 86 return _runFailingTest(classMirror, symbol); |
| 87 } else { |
| 88 return _runTest(classMirror, symbol); |
| 89 } |
49 }); | 90 }); |
50 return; | 91 return; |
51 } | 92 } |
52 // solo_test_ | 93 // solo_test_ |
53 if (memberName.startsWith('solo_test_')) { | 94 if (memberName.startsWith('solo_test_')) { |
54 solo_test(memberName, () { | 95 solo_test(memberName, () { |
55 return _runTest(classMirror, symbol); | 96 return _runTest(classMirror, symbol); |
56 }); | 97 }); |
57 } | 98 } |
58 // fail_test_ | 99 // fail_test_ |
59 if (memberName.startsWith('fail_')) { | 100 if (memberName.startsWith('fail_')) { |
60 test(memberName, () { | 101 test(memberName, () { |
61 return _runFailingTest(classMirror, symbol); | 102 return _runFailingTest(classMirror, symbol); |
62 }); | 103 }); |
63 } | 104 } |
64 // solo_fail_test_ | 105 // solo_fail_test_ |
65 if (memberName.startsWith('solo_fail_')) { | 106 if (memberName.startsWith('solo_fail_')) { |
66 solo_test(memberName, () { | 107 solo_test(memberName, () { |
67 return _runFailingTest(classMirror, symbol); | 108 return _runFailingTest(classMirror, symbol); |
68 }); | 109 }); |
69 } | 110 } |
70 }); | 111 }); |
71 }); | 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 } |
72 } | 119 } |
73 | 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 |
74 Future _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) { | 131 Future _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) { |
75 var invocationResult = null; | 132 var invocationResult = null; |
| 133 InstanceMirror closure; |
76 try { | 134 try { |
77 invocationResult = instanceMirror.invoke(symbol, []).reflectee; | 135 closure = instanceMirror.getField(symbol); |
78 } on NoSuchMethodError {} | 136 } on NoSuchMethodError {} |
79 if (invocationResult is Future) { | 137 |
80 return invocationResult; | 138 if (closure is ClosureMirror) { |
81 } else { | 139 invocationResult = closure.apply([]).reflectee; |
82 return new Future.value(invocationResult); | |
83 } | 140 } |
| 141 return new Future.value(invocationResult); |
84 } | 142 } |
85 | 143 |
86 /** | 144 /** |
87 * Run a test that is expected to fail, and confirm that it fails. | 145 * Run a test that is expected to fail, and confirm that it fails. |
88 * | 146 * |
89 * This properly handles the following cases: | 147 * This properly handles the following cases: |
90 * - The test fails by throwing an exception | 148 * - The test fails by throwing an exception |
91 * - The test returns a future which completes with an error. | 149 * - The test returns a future which completes with an error. |
92 * | 150 * |
93 * However, it does not handle the case where the test creates an asynchronous | 151 * However, it does not handle the case where the test creates an asynchronous |
(...skipping 14 matching lines...) Expand all Loading... |
108 | 166 |
109 /** | 167 /** |
110 * A marker annotation used to instruct dart2js to keep reflection information | 168 * A marker annotation used to instruct dart2js to keep reflection information |
111 * for the annotated classes. | 169 * for the annotated classes. |
112 */ | 170 */ |
113 class ReflectiveTest { | 171 class ReflectiveTest { |
114 const ReflectiveTest(); | 172 const ReflectiveTest(); |
115 } | 173 } |
116 | 174 |
117 /** | 175 /** |
118 * A marker annotation used to instruct dart2js to keep reflection information | 176 * A marker annotation used to annotate overridden test methods (so we cannot |
119 * for the annotated classes. | 177 * rename them to `fail_`) which are expected to fail at `assert` in the |
| 178 * checked mode. |
120 */ | 179 */ |
121 const ReflectiveTest reflectiveTest = const ReflectiveTest(); | 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 |