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 *     &#064;Test
37aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     public void a() {
38aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *         fail();
39aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     }
40aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *
41aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     &#064;Category(SlowTests.class)
42aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     &#064;Test
43aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     public void b() {
44aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     }
45aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *
46aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     &#064;Category({FastTests.class, SmokeTests.class})
47aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     &#064;Test
48aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     public void c() {
49aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     }
50b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * }
51aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *
52aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * &#064;Category({SlowTests.class, FastTests.class})
53b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * public static class B {
54aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     &#064;Test
55aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     public void d() {
56aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *     }
57b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * }
58aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin *
59b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * &#064;RunWith(Categories.class)
60b3823db9f1192d8c81345740b3e65bd6738ba55bBrett Chabot * &#064;IncludeCategory(SlowTests.class)
61aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * &#064;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 * &#064;RunWith(Categories.class)
71aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * &#064;IncludeCategory({FastTests.class, SmokeTests.class})
72aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin * &#064;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>&quot;[included categories] - [excluded categories]&quot;</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>&quot;categories [all]&quot;</tt> for all included categories and no excluded ones;
178aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin         *  <li> <tt>&quot;categories [all] - [A, B]&quot;</tt> for all included categories and given excluded ones;
179aeb93fc33cae3aadbb9b46083350ad2dc9aea645Paul Duffin         *  <li> <tt>&quot;categories [A, B] - [C, D]&quot;</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