001package org.junit.runners; 002 003import java.lang.annotation.ElementType; 004import java.lang.annotation.Inherited; 005import java.lang.annotation.Retention; 006import java.lang.annotation.RetentionPolicy; 007import java.lang.annotation.Target; 008import java.util.Collections; 009import java.util.List; 010 011import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; 012import org.junit.runner.Description; 013import org.junit.runner.Runner; 014import org.junit.runner.notification.RunNotifier; 015import org.junit.runners.model.InitializationError; 016import 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 */ 027public 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}