001package org.junit.runner.manipulation;
002
003import org.junit.runner.Description;
004import org.junit.runner.Request;
005
006/**
007 * The canonical case of filtering is when you want to run a single test method in a class. Rather
008 * than introduce runner API just for that one case, JUnit provides a general filtering mechanism.
009 * If you want to filter the tests to be run, extend <code>Filter</code> and apply an instance of
010 * your filter to the {@link org.junit.runner.Request} before running it (see
011 * {@link org.junit.runner.JUnitCore#run(Request)}. Alternatively, apply a <code>Filter</code> to
012 * a {@link org.junit.runner.Runner} before running tests (for example, in conjunction with
013 * {@link org.junit.runner.RunWith}.
014 *
015 * @since 4.0
016 */
017public abstract class Filter {
018    /**
019     * A null <code>Filter</code> that passes all tests through.
020     */
021    public static final Filter ALL = new Filter() {
022        @Override
023        public boolean shouldRun(Description description) {
024            return true;
025        }
026
027        @Override
028        public String describe() {
029            return "all tests";
030        }
031
032        @Override
033        public void apply(Object child) throws NoTestsRemainException {
034            // do nothing
035        }
036
037        @Override
038        public Filter intersect(Filter second) {
039            return second;
040        }
041    };
042
043    /**
044     * Returns a {@code Filter} that only runs the single method described by
045     * {@code desiredDescription}
046     */
047    public static Filter matchMethodDescription(final Description desiredDescription) {
048        return new Filter() {
049            @Override
050            public boolean shouldRun(Description description) {
051                if (description.isTest()) {
052                    return desiredDescription.equals(description);
053                }
054
055                // explicitly check if any children want to run
056                for (Description each : description.getChildren()) {
057                    if (shouldRun(each)) {
058                        return true;
059                    }
060                }
061                return false;
062            }
063
064            @Override
065            public String describe() {
066                return String.format("Method %s", desiredDescription.getDisplayName());
067            }
068        };
069    }
070
071
072    /**
073     * @param description the description of the test to be run
074     * @return <code>true</code> if the test should be run
075     */
076    public abstract boolean shouldRun(Description description);
077
078    /**
079     * Returns a textual description of this Filter
080     *
081     * @return a textual description of this Filter
082     */
083    public abstract String describe();
084
085    /**
086     * Invoke with a {@link org.junit.runner.Runner} to cause all tests it intends to run
087     * to first be checked with the filter. Only those that pass the filter will be run.
088     *
089     * @param child the runner to be filtered by the receiver
090     * @throws NoTestsRemainException if the receiver removes all tests
091     */
092    public void apply(Object child) throws NoTestsRemainException {
093        if (!(child instanceof Filterable)) {
094            return;
095        }
096        Filterable filterable = (Filterable) child;
097        filterable.filter(this);
098    }
099
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}