001 package org.junit.runners;
002
003 import java.lang.annotation.ElementType;
004 import java.lang.annotation.Inherited;
005 import java.lang.annotation.Retention;
006 import java.lang.annotation.RetentionPolicy;
007 import java.lang.annotation.Target;
008 import java.util.Collections;
009 import java.util.List;
010
011 import org.junit.internal.builders.AllDefaultPossibilitiesBuilder;
012 import org.junit.runner.Description;
013 import org.junit.runner.Runner;
014 import org.junit.runner.notification.RunNotifier;
015 import org.junit.runners.model.InitializationError;
016 import org.junit.runners.model.RunnerBuilder;
017
018 /**
019 * Using <code>Suite</code> as a runner allows you to manually
020 * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x
021 * static {@link junit.framework.Test} <code>suite()</code> method. To use it, annotate a class
022 * with <code>@RunWith(Suite.class)</code> and <code>@SuiteClasses({TestClass1.class, ...})</code>.
023 * When you run this class, it will run all the tests in all the suite classes.
024 *
025 * @since 4.0
026 */
027 public class Suite extends ParentRunner<Runner> {
028 /**
029 * Returns an empty suite.
030 */
031 public static Runner emptySuite() {
032 try {
033 return new Suite((Class<?>) null, new Class<?>[0]);
034 } catch (InitializationError e) {
035 throw new RuntimeException("This shouldn't be possible");
036 }
037 }
038
039 /**
040 * The <code>SuiteClasses</code> annotation specifies the classes to be run when a class
041 * annotated with <code>@RunWith(Suite.class)</code> is run.
042 */
043 @Retention(RetentionPolicy.RUNTIME)
044 @Target(ElementType.TYPE)
045 @Inherited
046 public @interface SuiteClasses {
047 /**
048 * @return the classes to be run
049 */
050 Class<?>[] value();
051 }
052
053 private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
054 SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class);
055 if (annotation == null) {
056 throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName()));
057 }
058 return annotation.value();
059 }
060
061 private final List<Runner> runners;
062
063 /**
064 * Called reflectively on classes annotated with <code>@RunWith(Suite.class)</code>
065 *
066 * @param klass the root class
067 * @param builder builds runners for classes in the suite
068 */
069 public Suite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
070 this(builder, klass, getAnnotatedClasses(klass));
071 }
072
073 /**
074 * Call this when there is no single root class (for example, multiple class names
075 * passed on the command line to {@link org.junit.runner.JUnitCore}
076 *
077 * @param builder builds runners for classes in the suite
078 * @param classes the classes in the suite
079 */
080 public Suite(RunnerBuilder builder, Class<?>[] classes) throws InitializationError {
081 this(null, builder.runners(null, classes));
082 }
083
084 /**
085 * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4.
086 *
087 * @param klass the root of the suite
088 * @param suiteClasses the classes in the suite
089 */
090 protected Suite(Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
091 this(new AllDefaultPossibilitiesBuilder(), klass, suiteClasses);
092 }
093
094 /**
095 * Called by this class and subclasses once the classes making up the suite have been determined
096 *
097 * @param builder builds runners for classes in the suite
098 * @param klass the root of the suite
099 * @param suiteClasses the classes in the suite
100 */
101 protected Suite(RunnerBuilder builder, Class<?> klass, Class<?>[] suiteClasses) throws InitializationError {
102 this(klass, builder.runners(klass, suiteClasses));
103 }
104
105 /**
106 * Called by this class and subclasses once the runners making up the suite have been determined
107 *
108 * @param klass root of the suite
109 * @param runners for each class in the suite, a {@link Runner}
110 */
111 protected Suite(Class<?> klass, List<Runner> runners) throws InitializationError {
112 super(klass);
113 this.runners = Collections.unmodifiableList(runners);
114 }
115
116 @Override
117 protected List<Runner> getChildren() {
118 return runners;
119 }
120
121 @Override
122 protected Description describeChild(Runner child) {
123 return child.getDescription();
124 }
125
126 @Override
127 protected void runChild(Runner runner, final RunNotifier notifier) {
128 runner.run(notifier);
129 }
130 }