001    package org.junit.rules;
002    
003    import org.junit.internal.runners.statements.FailOnTimeout;
004    import org.junit.runner.Description;
005    import org.junit.runners.model.Statement;
006    
007    import 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     */
040    public 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    }