Index: packages/analyzer/test/reflective_tests.dart |
diff --git a/packages/analyzer/test/reflective_tests.dart b/packages/analyzer/test/reflective_tests.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8180cf7800bd85737844932ba1fbdd051637b7fc |
--- /dev/null |
+++ b/packages/analyzer/test/reflective_tests.dart |
@@ -0,0 +1,121 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library reflective_tests; |
+ |
+@MirrorsUsed(metaTargets: 'ReflectiveTest') |
+import 'dart:mirrors'; |
+import 'dart:async'; |
+ |
+import 'package:unittest/unittest.dart'; |
+ |
+/** |
+ * Runs test methods existing in the given [type]. |
+ * |
+ * Methods with names starting with `test` are run using [test] function. |
+ * Methods with names starting with `solo_test` are run using [solo_test] function. |
+ * |
+ * Each method is run with a new instance of [type]. |
+ * So, [type] should have a default constructor. |
+ * |
+ * If [type] declares method `setUp`, it methods will be invoked before any test |
+ * method invocation. |
+ * |
+ * If [type] declares method `tearDown`, it will be invoked after any test |
+ * method invocation. If method returns [Future] to test some asyncronous |
+ * behavior, then `tearDown` will be invoked in `Future.complete`. |
+ */ |
+void runReflectiveTests(Type type) { |
+ ClassMirror classMirror = reflectClass(type); |
+ if (!classMirror.metadata.any((InstanceMirror annotation) => |
+ annotation.type.reflectedType == ReflectiveTest)) { |
+ String name = MirrorSystem.getName(classMirror.qualifiedName); |
+ throw new Exception('Class $name must have annotation "@reflectiveTest" ' |
+ 'in order to be run by runReflectiveTests.'); |
+ } |
+ String className = MirrorSystem.getName(classMirror.simpleName); |
+ group(className, () { |
+ classMirror.instanceMembers.forEach((symbol, memberMirror) { |
+ // we need only methods |
+ if (memberMirror is! MethodMirror || !memberMirror.isRegularMethod) { |
+ return; |
+ } |
+ String memberName = MirrorSystem.getName(symbol); |
+ // test_ |
+ if (memberName.startsWith('test_')) { |
+ test(memberName, () { |
+ return _runTest(classMirror, symbol); |
+ }); |
+ return; |
+ } |
+ // solo_test_ |
+ if (memberName.startsWith('solo_test_')) { |
+ solo_test(memberName, () { |
+ return _runTest(classMirror, symbol); |
+ }); |
+ } |
+ // fail_test_ |
+ if (memberName.startsWith('fail_')) { |
+ test(memberName, () { |
+ return _runFailingTest(classMirror, symbol); |
+ }); |
+ } |
+ // solo_fail_test_ |
+ if (memberName.startsWith('solo_fail_')) { |
+ solo_test(memberName, () { |
+ return _runFailingTest(classMirror, symbol); |
+ }); |
+ } |
+ }); |
+ }); |
+} |
+ |
+Future _invokeSymbolIfExists(InstanceMirror instanceMirror, Symbol symbol) { |
+ var invocationResult = null; |
+ try { |
+ invocationResult = instanceMirror.invoke(symbol, []).reflectee; |
+ } on NoSuchMethodError {} |
+ if (invocationResult is Future) { |
+ return invocationResult; |
+ } else { |
+ return new Future.value(invocationResult); |
+ } |
+} |
+ |
+/** |
+ * Run a test that is expected to fail, and confirm that it fails. |
+ * |
+ * This properly handles the following cases: |
+ * - The test fails by throwing an exception |
+ * - The test returns a future which completes with an error. |
+ * |
+ * However, it does not handle the case where the test creates an asynchronous |
+ * callback using expectAsync(), and that callback generates a failure. |
+ */ |
+Future _runFailingTest(ClassMirror classMirror, Symbol symbol) { |
+ return new Future(() => _runTest(classMirror, symbol)).then((_) { |
+ fail('Test passed - expected to fail.'); |
+ }, onError: (_) {}); |
+} |
+ |
+_runTest(ClassMirror classMirror, Symbol symbol) { |
+ InstanceMirror instanceMirror = classMirror.newInstance(new Symbol(''), []); |
+ return _invokeSymbolIfExists(instanceMirror, #setUp) |
+ .then((_) => instanceMirror.invoke(symbol, []).reflectee) |
+ .whenComplete(() => _invokeSymbolIfExists(instanceMirror, #tearDown)); |
+} |
+ |
+/** |
+ * A marker annotation used to instruct dart2js to keep reflection information |
+ * for the annotated classes. |
+ */ |
+class ReflectiveTest { |
+ const ReflectiveTest(); |
+} |
+ |
+/** |
+ * A marker annotation used to instruct dart2js to keep reflection information |
+ * for the annotated classes. |
+ */ |
+const ReflectiveTest reflectiveTest = const ReflectiveTest(); |