1package org.junit.runner.manipulation;
2
3import org.junit.runner.Description;
4import org.junit.runner.Request;
5
6/**
7 * The canonical case of filtering is when you want to run a single test method in a class. Rather
8 * than introduce runner API just for that one case, JUnit provides a general filtering mechanism.
9 * If you want to filter the tests to be run, extend <code>Filter</code> and apply an instance of
10 * your filter to the {@link org.junit.runner.Request} before running it (see
11 * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to
12 * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with
13 * {@link org.junit.runner.RunWith}.
14 *
15 * @since 4.0
16 */
17public abstract class Filter {
18    /**
19     * A null <code>Filter</code> that passes all tests through.
20     */
21    public static final Filter ALL = new Filter() {
22        @Override
23        public boolean shouldRun(Description description) {
24            return true;
25        }
26
27        @Override
28        public String describe() {
29            return "all tests";
30        }
31
32        @Override
33        public void apply(Object child) throws NoTestsRemainException {
34            // do nothing
35        }
36
37        @Override
38        public Filter intersect(Filter second) {
39            return second;
40        }
41    };
42
43    /**
44     * Returns a {@code Filter} that only runs the single method described by
45     * {@code desiredDescription}
46     */
47    public static Filter matchMethodDescription(final Description desiredDescription) {
48        return new Filter() {
49            @Override
50            public boolean shouldRun(Description description) {
51                if (description.isTest()) {
52                    return desiredDescription.equals(description);
53                }
54
55                // explicitly check if any children want to run
56                for (Description each : description.getChildren()) {
57                    if (shouldRun(each)) {
58                        return true;
59                    }
60                }
61                return false;
62            }
63
64            @Override
65            public String describe() {
66                return String.format("Method %s", desiredDescription.getDisplayName());
67            }
68        };
69    }
70
71
72    /**
73     * @param description the description of the test to be run
74     * @return <code>true</code> if the test should be run
75     */
76    public abstract boolean shouldRun(Description description);
77
78    /**
79     * Returns a textual description of this Filter
80     *
81     * @return a textual description of this Filter
82     */
83    public abstract String describe();
84
85    /**
86     * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run
87     * to first be checked with the filter. Only those that pass the filter will be run.
88     *
89     * @param child the runner to be filtered by the receiver
90     * @throws NoTestsRemainException if the receiver removes all tests
91     */
92    public void apply(Object child) throws NoTestsRemainException {
93        if (!(child instanceof Filterable)) {
94            return;
95        }
96        Filterable filterable = (Filterable) child;
97        filterable.filter(this);
98    }
99
100    /**
101     * Returns a new Filter that accepts the intersection of the tests accepted
102     * by this Filter and {@code second}
103     */
104    public Filter intersect(final Filter second) {
105        if (second == this || second == ALL) {
106            return this;
107        }
108        final Filter first = this;
109        return new Filter() {
110            @Override
111            public boolean shouldRun(Description description) {
112                return first.shouldRun(description)
113                        && second.shouldRun(description);
114            }
115
116            @Override
117            public String describe() {
118                return first.describe() + " and " + second.describe();
119            }
120        };
121    }
122}
123