    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    }