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 reflective_tests; |
| 6 |
| 7 @MirrorsUsed(metaTargets: 'ReflectiveTest') |
| 8 import 'dart:mirrors'; |
| 9 import 'dart:async'; |
| 10 |
| 11 import 'package:unittest/unittest.dart'; |
| 12 |
| 13 /** |
| 14 * Runs test methods existing in the given [type]. |
| 15 * |
| 16 * 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. |
| 18 * |
| 19 * Each method is run with a new instance of [type]. |
| 20 * So, [type] should have a default constructor. |
| 21 * |
| 22 * If [type] declares method `setUp`, it methods will be invoked before any test |
| 23 * method invocation. |
| 24 * |
| 25 * If [type] declares method `tearDown`, it will be invoked after any test |
| 26 * method invocation. If method returns [Future] to test some asyncronous |
| 27 * behavior, then `tearDown` will be invoked in `Future.complete`. |
| 28 */ |
| 29 void runReflectiveTests(Type type) { |
| 30 ClassMirror classMirror = reflectClass(type); |
| 31 if (!classMirror.metadata.any((InstanceMirror annotation) => |
| 32 annotation.type.reflectedType == ReflectiveTest)) { |
| 33 String name = MirrorSystem.getName(classMirror.qualifiedName); |
| 34 throw new Exception('Class $name must have annotation "@reflectiveTest" ' |
| 35 'in order to be run by runReflectiveTests.'); |
| 36 } |
| 37 String className = MirrorSystem.getName(classMirror.simpleName); |
| 38 group(className, () { |
| 39 classMirror.instanceMembers.forEach((symbol, memberMirror) { |
| 40 // we need only methods |
| 41 if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) { |
| 42 return; |
| 43 } |
| 44 String memberName = MirrorSystem.getName(symbol); |
| 45 // test_ |
| 46 if (memberName.startsWith('test_')) { |
| 47 test(memberName, () { |
| 48 return _runTest(classMirror, symbol); |
| 49 }); |
| 50 return; |
| 51 } |
| 52 // solo_test_ |
| 53 if (memberName.startsWith('solo_test_')) { |
| 54 solo_test(memberName, () { |
| 55 return _runTest(classMirror, symbol); |
| 56 }); |
| 57 } |
| 58 // fail_test_ |
| 59 if (memberName.startsWith('fail_')) { |
| 60 test(memberName, () { |
| 61 return _runFailingTest(classMirror, symbol); |
| 62 }); |
| 63 } |
| 64 // solo_fail_test_ |
| 65 if (memberName.startsWith('solo_fail_')) { |
| 66 solo_test(memberName, () { |
| 67 return _runFailingTest(classMirror, symbol); |
| 68 }); |
| 69 } |
| 70 }); |
| 71 }); |
| 72 } |
| 73 |
| 74 Future _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) { |
| 75 var invocationResult = null; |
| 76 try { |
| 77 invocationResult = instanceMirror.invoke(symbol, []).reflectee; |
| 78 } on NoSuchMethodError {} |
| 79 if (invocationResult is Future) { |
| 80 return invocationResult; |
| 81 } else { |
| 82 return new Future.value(invocationResult); |
| 83 } |
| 84 } |
| 85 |
| 86 /** |
| 87 * Run a test that is expected to fail, and confirm that it fails. |
| 88 * |
| 89 * This properly handles the following cases: |
| 90 * - The test fails by throwing an exception |
| 91 * - The test returns a future which completes with an error. |
| 92 * |
| 93 * However, it does not handle the case where the test creates an asynchronous |
| 94 * callback using expectAsync(), and that callback generates a failure. |
| 95 */ |
| 96 Future _runFailingTest(ClassMirror classMirror, Symbol symbol) { |
| 97 return new Future(() => _runTest(classMirror, symbol)).then((_) { |
| 98 fail('Test passed - expected to fail.'); |
| 99 }, onError: (_) {}); |
| 100 } |
| 101 |
| 102 _runTest(ClassMirror classMirror, Symbol symbol) { |
| 103 InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []); |
| 104 return _invokeSymbolIfExists(instanceMirror, #setUp) |
| 105 .then((_) => instanceMirror.invoke(symbol, []).reflectee) |
| 106 .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown)); |
| 107 } |
| 108 |
| 109 /** |
| 110 * A marker annotation used to instruct dart2js to keep reflection information |
| 111 * for the annotated classes. |
| 112 */ |
| 113 class ReflectiveTest { |
| 114 const ReflectiveTest(); |
| 115 } |
| 116 |
| 117 /** |
| 118 * A marker annotation used to instruct dart2js to keep reflection information |
| 119 * for the annotated classes. |
| 120 */ |
| 121 const ReflectiveTest reflectiveTest = const ReflectiveTest(); |
OLD | NEW |