a38a503d86
R=brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org//849863002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@42821 260f80e4-7a28-3924-810f-c04153c831b5
136 lines
4.2 KiB
Dart
136 lines
4.2 KiB
Dart
// 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 catch (e) {
|
|
}
|
|
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();
|