001package org.junit.rules;
002
003import org.junit.internal.runners.statements.FailOnTimeout;
004import org.junit.runner.Description;
005import org.junit.runners.model.Statement;
006
007import java.util.concurrent.TimeUnit;
008
009/**
010 * The Timeout Rule applies the same timeout to all test methods in a class:
011 * <pre>
012 * public static class HasGlobalLongTimeout {
013 *
014 *  &#064;Rule
015 *  public Timeout globalTimeout = Timeout.millis(20);
016 *
017 *  &#064;Test
018 *  public void run1() throws InterruptedException {
019 *      Thread.sleep(100);
020 *  }
021 *
022 *  &#064;Test
023 *  public void infiniteLoop() {
024 *      while (true) {}
025 *  }
026 * }
027 * </pre>
028 * <p>
029 * Each test is run in a new thread. If the specified timeout elapses before
030 * the test completes, its execution is interrupted via {@link Thread#interrupt()}.
031 * This happens in interruptable I/O and locks, and methods in {@link Object}
032 * and {@link Thread} throwing {@link InterruptedException}.
033 * <p>
034 * A specified timeout of 0 will be interpreted as not set, however tests will
035 * still launch from separate threads. This can be useful for disabling timeouts
036 * in environments where they are dynamically set based on some property.
037 *
038 * @since 4.7
039 */
040public class Timeout implements TestRule {
041    private final long timeout;
042    private final TimeUnit timeUnit;
043    private final boolean lookForStuckThread;
044
045    /**
046     * Returns a new builder for building an instance.
047     *
048     * @since 4.12
049     */
050    public static Builder builder() {
051        return new Builder();
052    }
053
054    /**
055     * Create a {@code Timeout} instance with the timeout specified
056     * in milliseconds.
057     * <p>
058     * This constructor is deprecated.
059     * <p>
060     * Instead use {@link #Timeout(long, java.util.concurrent.TimeUnit)},
061     * {@link Timeout#millis(long)}, or {@link Timeout#seconds(long)}.
062     *
063     * @param millis the maximum time in milliseconds to allow the
064     * test to run before it should timeout
065     */
066    @Deprecated
067    public Timeout(int millis) {
068        this(millis, TimeUnit.MILLISECONDS);
069    }
070
071    /**
072     * Create a {@code Timeout} instance with the timeout specified
073     * at the timeUnit of granularity of the provided {@code TimeUnit}.
074     *
075     * @param timeout the maximum time to allow the test to run
076     * before it should timeout
077     * @param timeUnit the time unit for the {@code timeout}
078     * @since 4.12
079     */
080    public Timeout(long timeout, TimeUnit timeUnit) {
081        this.timeout = timeout;
082        this.timeUnit = timeUnit;
083        lookForStuckThread = false;
084    }
085
086    /**
087     * Create a {@code Timeout} instance initialized with values from
088     * a builder.
089     *
090     * @since 4.12
091     */
092    protected Timeout(Builder builder) {
093        timeout = builder.getTimeout();
094        timeUnit = builder.getTimeUnit();
095        lookForStuckThread = builder.getLookingForStuckThread();
096    }
097
098    /**
099     * Creates a {@link Timeout} that will timeout a test after the
100     * given duration, in milliseconds.
101     *
102     * @since 4.12
103     */
104    public static Timeout millis(long millis) {
105        return new Timeout(millis, TimeUnit.MILLISECONDS);
106    }
107
108    /**
109     * Creates a {@link Timeout} that will timeout a test after the
110     * given duration, in seconds.
111     *
112     * @since 4.12
113     */
114    public static Timeout seconds(long seconds) {
115        return new Timeout(seconds, TimeUnit.SECONDS);
116    }
117
118    /**
119     * Gets the timeout configured for this rule, in the given units.
120     *
121     * @since 4.12
122     */
123    protected final long getTimeout(TimeUnit unit) {
124        return unit.convert(timeout, timeUnit);
125    }
126
127    /**
128     * Gets whether this {@code Timeout} will look for a stuck thread
129     * when the test times out.
130     *
131     * @since 4.12
132     */
133    protected final boolean getLookingForStuckThread() {
134        return lookForStuckThread;
135    }
136
137    /**
138     * Creates a {@link Statement} that will run the given
139     * {@code statement}, and timeout the operation based
140     * on the values configured in this rule. Subclasses
141     * can override this method for different behavior.
142     *
143     * @since 4.12
144     */
145    protected Statement createFailOnTimeoutStatement(
146            Statement statement) throws Exception {
147        return FailOnTimeout.builder()
148            .withTimeout(timeout, timeUnit)
149            .withLookingForStuckThread(lookForStuckThread)
150            .build(statement);
151    }
152
153    public Statement apply(Statement base, Description description) {
154        try {
155            return createFailOnTimeoutStatement(base);
156        } catch (final Exception e) {
157            return new Statement() {
158                @Override public void evaluate() throws Throwable {
159                    throw new RuntimeException("Invalid parameters for Timeout", e);
160                }
161            };
162        }
163    }
164
165    /**
166     * Builder for {@link Timeout}.
167     *
168     * @since 4.12
169     */
170    public static class Builder {
171        private boolean lookForStuckThread = false;
172        private long timeout = 0;
173        private TimeUnit timeUnit = TimeUnit.SECONDS;
174
175        protected Builder() {
176        }
177
178        /**
179         * Specifies the time to wait before timing out the test.
180         *
181         * <p>If this is not called, or is called with a
182         * {@code timeout} of {@code 0}, the returned {@code Timeout}
183         * rule instance will cause the tests to wait forever to
184         * complete, however the tests will still launch from a
185         * separate thread. This can be useful for disabling timeouts
186         * in environments where they are dynamically set based on
187         * some property.
188         *
189         * @param timeout the maximum time to wait
190         * @param unit the time unit of the {@code timeout} argument
191         * @return {@code this} for method chaining.
192         */
193        public Builder withTimeout(long timeout, TimeUnit unit) {
194            this.timeout = timeout;
195            this.timeUnit = unit;
196            return this;
197        }
198
199        protected long getTimeout() {
200            return timeout;
201        }
202
203        protected TimeUnit getTimeUnit()  {
204            return timeUnit;
205        }
206
207        /**
208         * Specifies whether to look for a stuck thread.  If a timeout occurs and this
209         * feature is enabled, the rule will look for a thread that appears to be stuck
210         * and dump its backtrace.  This feature is experimental.  Behavior may change
211         * after the 4.12 release in response to feedback.
212         *
213         * @param enable {@code true} to enable the feature
214         * @return {@code this} for method chaining.
215         */
216        public Builder withLookingForStuckThread(boolean enable) {
217            this.lookForStuckThread = enable;
218            return this;
219        }
220
221        protected boolean getLookingForStuckThread() {
222            return lookForStuckThread;
223        }
224
225
226        /**
227         * Builds a {@link Timeout} instance using the values in this builder.,
228         */
229        public Timeout build() {
230            return new Timeout(this);
231        }
232    }
233}