| 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 |