1b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotpackage org.junit.experimental.categories; 2b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot 3b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.lang.annotation.Retention; 4b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport java.lang.annotation.RetentionPolicy; 5aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffinimport java.util.Collections; 6aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffinimport java.util.HashSet; 7aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffinimport java.util.Set; 8b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot 9b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.runner.Description; 10b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.runner.manipulation.Filter; 11b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.runner.manipulation.NoTestsRemainException; 12b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.runners.Suite; 13b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.runners.model.InitializationError; 14b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotimport org.junit.runners.model.RunnerBuilder; 15b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot 16b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot/** 17b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * From a given set of test classes, runs only the classes and methods that are 18b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * annotated with either the category given with the @IncludeCategory 19b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * annotation, or a subtype of that category. 20aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <p> 21b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * Note that, for now, annotating suites with {@code @Category} has no effect. 22b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * Categories must be annotated on the direct method or class. 23aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <p> 24b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * Example: 25b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * <pre> 26b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * public interface FastTests { 27b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * } 28aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 29b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * public interface SlowTests { 30b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * } 31aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 32aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * public interface SmokeTests 33aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * } 34aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 35b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * public static class A { 36aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Test 37aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * public void a() { 38aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * fail(); 39aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * } 40aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 41aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Category(SlowTests.class) 42aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Test 43aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * public void b() { 44aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * } 45aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 46aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Category({FastTests.class, SmokeTests.class}) 47aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Test 48aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * public void c() { 49aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * } 50b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * } 51aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 52aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Category({SlowTests.class, FastTests.class}) 53b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * public static class B { 54aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @Test 55aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * public void d() { 56aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * } 57b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * } 58aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 59b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * @RunWith(Categories.class) 60b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * @IncludeCategory(SlowTests.class) 61aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @SuiteClasses({A.class, B.class}) 62b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * // Note that Categories is a kind of Suite 63b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * public static class SlowTestSuite { 64aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * // Will run A.b and B.d, but not A.a and A.c 65aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * } 66aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * </pre> 67aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <p> 68aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * Example to run multiple categories: 69aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <pre> 70aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @RunWith(Categories.class) 71aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @IncludeCategory({FastTests.class, SmokeTests.class}) 72aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @SuiteClasses({A.class, B.class}) 73aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * public static class FastOrSmokeTestSuite { 74aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * // Will run A.c and B.d, but not A.b because it is not any of FastTests or SmokeTests 75b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * } 76b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * </pre> 77aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 78aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @version 4.12 79aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @see <a href="https://github.com/junit-team/junit/wiki/Categories">Categories at JUnit wiki</a> 80b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot */ 81b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabotpublic class Categories extends Suite { 82aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 83aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin @Retention(RetentionPolicy.RUNTIME) 84aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public @interface IncludeCategory { 85aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 86aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * Determines the tests to run that are annotated with categories specified in 87aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * the value of this annotation or their subtypes unless excluded with {@link ExcludeCategory}. 88aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 89aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public Class<?>[] value() default {}; 90aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 91aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 92aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * If <tt>true</tt>, runs tests annotated with <em>any</em> of the categories in 93aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * {@link IncludeCategory#value()}. Otherwise, runs tests only if annotated with <em>all</em> of the categories. 94aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 95aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public boolean matchAny() default true; 96aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 97aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 98aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin @Retention(RetentionPolicy.RUNTIME) 99aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public @interface ExcludeCategory { 100aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 101aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * Determines the tests which do not run if they are annotated with categories specified in the 102aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * value of this annotation or their subtypes regardless of being included in {@link IncludeCategory#value()}. 103aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 104aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public Class<?>[] value() default {}; 105aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 106aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 107aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * If <tt>true</tt>, the tests annotated with <em>any</em> of the categories in {@link ExcludeCategory#value()} 108aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * do not run. Otherwise, the tests do not run if and only if annotated with <em>all</em> categories. 109aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 110aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public boolean matchAny() default true; 111aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 112aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 113aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static class CategoryFilter extends Filter { 114aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private final Set<Class<?>> included; 115aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private final Set<Class<?>> excluded; 116aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private final boolean includedAny; 117aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private final boolean excludedAny; 118aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 119aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter include(boolean matchAny, Class<?>... categories) { 120aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (hasNull(categories)) { 121aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin throw new NullPointerException("has null category"); 122aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 123aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return categoryFilter(matchAny, createSet(categories), true, null); 124aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 125aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 126aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter include(Class<?> category) { 127aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return include(true, category); 128aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 129aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 130aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter include(Class<?>... categories) { 131aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return include(true, categories); 132aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 133aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 134aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter exclude(boolean matchAny, Class<?>... categories) { 135aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (hasNull(categories)) { 136aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin throw new NullPointerException("has null category"); 137aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 138aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return categoryFilter(true, null, matchAny, createSet(categories)); 139aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 140aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 141aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter exclude(Class<?> category) { 142aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return exclude(true, category); 143aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 144aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 145aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter exclude(Class<?>... categories) { 146aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return exclude(true, categories); 147aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 148aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 149aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public static CategoryFilter categoryFilter(boolean matchAnyInclusions, Set<Class<?>> inclusions, 150aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin boolean matchAnyExclusions, Set<Class<?>> exclusions) { 151aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return new CategoryFilter(matchAnyInclusions, inclusions, matchAnyExclusions, exclusions); 152aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 153aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 154aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin protected CategoryFilter(boolean matchAnyIncludes, Set<Class<?>> includes, 155aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin boolean matchAnyExcludes, Set<Class<?>> excludes) { 156aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin includedAny = matchAnyIncludes; 157aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin excludedAny = matchAnyExcludes; 158aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin included = copyAndRefine(includes); 159aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin excluded = copyAndRefine(excludes); 160aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 161aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 162aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 163aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @see #toString() 164aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 165aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin @Override 166aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public String describe() { 167aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return toString(); 168aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 169aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 170aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 171aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * Returns string in the form <tt>"[included categories] - [excluded categories]"</tt>, where both 172aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * sets have comma separated names of categories. 173aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * 174aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @return string representation for the relative complement of excluded categories set 175aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * in the set of included categories. Examples: 176aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <ul> 177aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <li> <tt>"categories [all]"</tt> for all included categories and no excluded ones; 178aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <li> <tt>"categories [all] - [A, B]"</tt> for all included categories and given excluded ones; 179aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * <li> <tt>"categories [A, B] - [C, D]"</tt> for given included categories and given excluded ones. 180aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * </ul> 181aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @see Class#toString() name of category 182aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 183aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin @Override public String toString() { 184aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin StringBuilder description= new StringBuilder("categories ") 185aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin .append(included.isEmpty() ? "[all]" : included); 186aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (!excluded.isEmpty()) { 187aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin description.append(" - ").append(excluded); 188aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 189aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return description.toString(); 190aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 191aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 192aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin @Override 193aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public boolean shouldRun(Description description) { 194aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (hasCorrectCategoryAnnotation(description)) { 195aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 196aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 197aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 198aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Description each : description.getChildren()) { 199aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (shouldRun(each)) { 200aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 201aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 202aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 203aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 204aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 205aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 206aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 207aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private boolean hasCorrectCategoryAnnotation(Description description) { 208aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin final Set<Class<?>> childCategories= categories(description); 209aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 210aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin // If a child has no categories, immediately return. 211aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (childCategories.isEmpty()) { 212aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return included.isEmpty(); 213aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 214aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 215aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (!excluded.isEmpty()) { 216aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (excludedAny) { 217aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (matchesAnyParentCategories(childCategories, excluded)) { 218aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 219aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 220aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } else { 221aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (matchesAllParentCategories(childCategories, excluded)) { 222aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 223aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 224aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 225aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 226aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 227aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (included.isEmpty()) { 228aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin // Couldn't be excluded, and with no suite's included categories treated as should run. 229aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 230aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } else { 231aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (includedAny) { 232aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return matchesAnyParentCategories(childCategories, included); 233aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } else { 234aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return matchesAllParentCategories(childCategories, included); 235aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 236aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 237aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 238aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 239aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 240aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @return <tt>true</tt> if at least one (any) parent category match a child, otherwise <tt>false</tt>. 241aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * If empty <tt>parentCategories</tt>, returns <tt>false</tt>. 242aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 243aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private boolean matchesAnyParentCategories(Set<Class<?>> childCategories, Set<Class<?>> parentCategories) { 244aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Class<?> parentCategory : parentCategories) { 245aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (hasAssignableTo(childCategories, parentCategory)) { 246aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 247aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 248aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 249aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 250aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 251aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 252aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin /** 253aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * @return <tt>false</tt> if at least one parent category does not match children, otherwise <tt>true</tt>. 254aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * If empty <tt>parentCategories</tt>, returns <tt>true</tt>. 255aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin */ 256aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private boolean matchesAllParentCategories(Set<Class<?>> childCategories, Set<Class<?>> parentCategories) { 257aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Class<?> parentCategory : parentCategories) { 258aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (!hasAssignableTo(childCategories, parentCategory)) { 259aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 260aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 261aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 262aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 263aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 264aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 265aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Set<Class<?>> categories(Description description) { 266aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Set<Class<?>> categories= new HashSet<Class<?>>(); 267aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Collections.addAll(categories, directCategories(description)); 268aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Collections.addAll(categories, directCategories(parentDescription(description))); 269aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return categories; 270aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 271aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 272aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Description parentDescription(Description description) { 273aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Class<?> testClass= description.getTestClass(); 274aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return testClass == null ? null : Description.createSuiteDescription(testClass); 275aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 276aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 277aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Class<?>[] directCategories(Description description) { 278aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (description == null) { 279aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return new Class<?>[0]; 280aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 281aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 282aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Category annotation= description.getAnnotation(Category.class); 283aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return annotation == null ? new Class<?>[0] : annotation.value(); 284aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 285aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 286aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Set<Class<?>> copyAndRefine(Set<Class<?>> classes) { 287aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin HashSet<Class<?>> c= new HashSet<Class<?>>(); 288aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (classes != null) { 289aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin c.addAll(classes); 290aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 291aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin c.remove(null); 292aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return c; 293aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 294aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 295aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static boolean hasNull(Class<?>... classes) { 296aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (classes == null) return false; 297aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Class<?> clazz : classes) { 298aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (clazz == null) { 299aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 300aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 301aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 302aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 303aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 304aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 305aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 306aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin public Categories(Class<?> klass, RunnerBuilder builder) throws InitializationError { 307aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin super(klass, builder); 308aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin try { 309aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Set<Class<?>> included= getIncludedCategory(klass); 310aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Set<Class<?>> excluded= getExcludedCategory(klass); 311aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin boolean isAnyIncluded= isAnyIncluded(klass); 312aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin boolean isAnyExcluded= isAnyExcluded(klass); 313aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 314aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin filter(CategoryFilter.categoryFilter(isAnyIncluded, included, isAnyExcluded, excluded)); 315aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } catch (NoTestsRemainException e) { 316aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin throw new InitializationError(e); 317aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 318aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription()); 319aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 320aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 321aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Set<Class<?>> getIncludedCategory(Class<?> klass) { 322aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); 323aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return createSet(annotation == null ? null : annotation.value()); 324aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 325aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 326aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static boolean isAnyIncluded(Class<?> klass) { 327aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); 328aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return annotation == null || annotation.matchAny(); 329aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 330aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 331aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Set<Class<?>> getExcludedCategory(Class<?> klass) { 332aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); 333aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return createSet(annotation == null ? null : annotation.value()); 334aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 335aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 336aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static boolean isAnyExcluded(Class<?> klass) { 337aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); 338aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return annotation == null || annotation.matchAny(); 339aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 340aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 341aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { 342aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (!canHaveCategorizedChildren(description)) { 343aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin assertNoDescendantsHaveCategoryAnnotations(description); 344aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 345aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Description each : description.getChildren()) { 346aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin assertNoCategorizedDescendentsOfUncategorizeableParents(each); 347aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 348aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 349aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 350aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError { 351aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Description each : description.getChildren()) { 352aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (each.getAnnotation(Category.class) != null) { 353aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods."); 354aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 355aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin assertNoDescendantsHaveCategoryAnnotations(each); 356aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 357aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 358aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 359aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin // If children have names like [0], our current magical category code can't determine their parentage. 360aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static boolean canHaveCategorizedChildren(Description description) { 361aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (Description each : description.getChildren()) { 362aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (each.getTestClass() == null) { 363aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 364aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 365aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 366aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 367aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 368aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 369aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static boolean hasAssignableTo(Set<Class<?>> assigns, Class<?> to) { 370aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin for (final Class<?> from : assigns) { 371aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (to.isAssignableFrom(from)) { 372aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return true; 373aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 374aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 375aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return false; 376aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 377aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin 378aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin private static Set<Class<?>> createSet(Class<?>... t) { 379aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin final Set<Class<?>> set= new HashSet<Class<?>>(); 380aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin if (t != null) { 381aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin Collections.addAll(set, t); 382aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 383aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin return set; 384aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin } 385aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin} 386