001: package org.junit.runners;
002:
003: import java.lang.annotation.ElementType;
004: import java.lang.annotation.Retention;
005: import java.lang.annotation.RetentionPolicy;
006: import java.lang.annotation.Target;
007: import java.util.HashSet;
008: import java.util.Set;
009:
010: import org.junit.internal.runners.CompositeRunner;
011: import org.junit.internal.runners.InitializationError;
012: import org.junit.internal.runners.MethodValidator;
013: import org.junit.internal.runners.TestClass;
014: import org.junit.internal.runners.ClassRoadie;
015: import org.junit.runner.Request;
016: import org.junit.runner.Runner;
017: import org.junit.runner.notification.RunNotifier;
018:
019: /**
020: * Using <code>Suite</code> as a runner allows you to manually
021: * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x
022: * static {@link junit.framework.Test} <code>suite()</code> method. To use it, annotate a class
023: * with <code>@RunWith(Suite.class)</code> and <code>@SuiteClasses(TestClass1.class, ...)</code>.
024: * When you run this class, it will run all the tests in all the suite classes.
025: */
026: public class Suite extends CompositeRunner {
027: /**
028: * The <code>SuiteClasses</code> annotation specifies the classes to be run when a class
029: * annotated with <code>@RunWith(Suite.class)</code> is run.
030: */
031: @Retention(RetentionPolicy.RUNTIME)
032: @Target(ElementType.TYPE)
033: public @interface SuiteClasses {
034: public Class<?>[] value();
035: }
036:
037: /**
038: * Internal use only.
039: */
040: public Suite(Class<?> klass) throws InitializationError {
041: this (klass, getAnnotatedClasses(klass));
042: }
043:
044: // This won't work correctly in the face of concurrency. For that we need to
045: // add parameters to getRunner(), which would be much more complicated.
046: private static Set<Class<?>> parents = new HashSet<Class<?>>();
047: private TestClass fTestClass;
048:
049: protected Suite(Class<?> klass, Class<?>[] annotatedClasses)
050: throws InitializationError {
051: // we need to add parent be
052: super (klass.getName());
053:
054: addParent(klass);
055: for (Class<?> each : annotatedClasses) {
056: Runner childRunner = Request.aClass(each).getRunner();
057: if (childRunner != null)
058: add(childRunner);
059: }
060: removeParent(klass);
061:
062: fTestClass = new TestClass(klass);
063: MethodValidator methodValidator = new MethodValidator(
064: fTestClass);
065: methodValidator.validateStaticMethods();
066: methodValidator.assertValid();
067: }
068:
069: private Class<?> addParent(Class<?> parent)
070: throws InitializationError {
071: if (!parents.add(parent))
072: throw new InitializationError(
073: String
074: .format(
075: "class '%s' (possibly indirectly) contains itself as a SuiteClass",
076: parent.getName()));
077: return parent;
078: }
079:
080: private void removeParent(Class<?> klass) {
081: parents.remove(klass);
082: }
083:
084: private static Class<?>[] getAnnotatedClasses(Class<?> klass)
085: throws InitializationError {
086: SuiteClasses annotation = klass
087: .getAnnotation(SuiteClasses.class);
088: if (annotation == null)
089: throw new InitializationError(String.format(
090: "class '%s' must have a SuiteClasses annotation",
091: klass.getName()));
092: return annotation.value();
093: }
094:
095: protected void validate(MethodValidator methodValidator) {
096: methodValidator.validateStaticMethods();
097: methodValidator.validateInstanceMethods();
098: }
099:
100: @Override
101: public void run(final RunNotifier notifier) {
102: new ClassRoadie(notifier, fTestClass, getDescription(),
103: new Runnable() {
104: public void run() {
105: runChildren(notifier);
106: }
107: }).runProtected();
108: }
109: }
|