|  | // 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(); |