001package org.junit.rules;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.List;
006
007import org.junit.Rule;
008import org.junit.runner.Description;
009import org.junit.runners.model.Statement;
010
011/**
012 * The {@code RuleChain} can be used for creating composite rules. You create a
013 * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
014 * {@link #around(TestRule)}:
015 *
016 * <pre>
017 * public abstract class CompositeRules {
018 *   public static TestRule extendedLogging() {
019 *     return RuleChain.outerRule(new LoggingRule("outer rule"))
020 *                     .around(new LoggingRule("middle rule"))
021 *                     .around(new LoggingRule("inner rule"));
022 *   }
023 * }
024 * </pre>
025 *
026 * <pre>
027 * public class UseRuleChain {
028 *   &#064;Rule
029 *   public final TestRule extendedLogging = CompositeRules.extendedLogging();
030 *
031 *   &#064;Test
032 *   public void example() {
033 *     assertTrue(true);
034 *   }
035 * }
036 * </pre>
037 *
038 * writes the log
039 *
040 * <pre>
041 * starting outer rule
042 * starting middle rule
043 * starting inner rule
044 * finished inner rule
045 * finished middle rule
046 * finished outer rule
047 * </pre>
048 *
049 * In older versions of JUnit (before 4.13) {@code RuleChain} was used for
050 * ordering rules. We recommend to not use it for this purpose anymore. You can
051 * use the attribute {@code order} of the annotation {@link Rule#order() Rule}
052 * or {@link org.junit.ClassRule#order() ClassRule} for ordering rules.
053 *
054 * @see org.junit.Rule#order()
055 * @see org.junit.ClassRule#order()
056 * @since 4.10
057 */
058public class RuleChain implements TestRule {
059    private static final RuleChain EMPTY_CHAIN = new RuleChain(
060            Collections.<TestRule>emptyList());
061
062    private List<TestRule> rulesStartingWithInnerMost;
063
064    /**
065     * Returns a {@code RuleChain} without a {@link TestRule}. This method may
066     * be the starting point of a {@code RuleChain}.
067     *
068     * @return a {@code RuleChain} without a {@link TestRule}.
069     */
070    public static RuleChain emptyRuleChain() {
071        return EMPTY_CHAIN;
072    }
073
074    /**
075     * Returns a {@code RuleChain} with a single {@link TestRule}. This method
076     * is the usual starting point of a {@code RuleChain}.
077     *
078     * @param outerRule the outer rule of the {@code RuleChain}.
079     * @return a {@code RuleChain} with a single {@link TestRule}.
080     */
081    public static RuleChain outerRule(TestRule outerRule) {
082        return emptyRuleChain().around(outerRule);
083    }
084
085    private RuleChain(List<TestRule> rules) {
086        this.rulesStartingWithInnerMost = rules;
087    }
088
089    /**
090     * Create a new {@code RuleChain}, which encloses the given {@link TestRule} with
091     * the rules of the current {@code RuleChain}.
092     *
093     * @param enclosedRule the rule to enclose; must not be {@code null}.
094     * @return a new {@code RuleChain}.
095     * @throws NullPointerException if the argument {@code enclosedRule} is {@code null}
096     */
097    public RuleChain around(TestRule enclosedRule) {
098        if (enclosedRule == null) {
099            throw new NullPointerException("The enclosed rule must not be null");
100        }
101        List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
102        rulesOfNewChain.add(enclosedRule);
103        rulesOfNewChain.addAll(rulesStartingWithInnerMost);
104        return new RuleChain(rulesOfNewChain);
105    }
106
107    /**
108     * {@inheritDoc}
109     */
110    public Statement apply(Statement base, Description description) {
111        return new RunRules(base, rulesStartingWithInnerMost, description);
112    }
113}