001    package org.junit.rules;
002    
003    import java.lang.management.ManagementFactory;
004    import java.lang.management.RuntimeMXBean;
005    import java.util.List;
006    
007    import org.junit.runner.Description;
008    import org.junit.runners.model.Statement;
009    
010    /**
011     * The {@code DisableOnDebug} Rule allows you to label certain rules to be
012     * disabled when debugging.
013     * <p>
014     * The most illustrative use case is for tests that make use of the
015     * {@link Timeout} rule, when ran in debug mode the test may terminate on
016     * timeout abruptly during debugging. Developers may disable the timeout, or
017     * increase the timeout by making a code change on tests that need debugging and
018     * remember revert the change afterwards or rules such as {@link Timeout} that
019     * may be disabled during debugging may be wrapped in a {@code DisableOnDebug}.
020     * <p>
021     * The important benefit of this feature is that you can disable such rules
022     * without any making any modifications to your test class to remove them during
023     * debugging.
024     * <p>
025     * This does nothing to tackle timeouts or time sensitive code under test when
026     * debugging and may make this less useful in such circumstances.
027     * <p>
028     * Example usage:
029     * 
030     * <pre>
031     * public static class DisableTimeoutOnDebugSampleTest {
032     * 
033     *     &#064;Rule
034     *     public TestRule timeout = new DisableOnDebug(new Timeout(20));
035     * 
036     *     &#064;Test
037     *     public void myTest() {
038     *         int i = 0;
039     *         assertEquals(0, i); // suppose you had a break point here to inspect i
040     *     }
041     * }
042     * </pre>
043     * 
044     * @since 4.12
045     */
046    public class DisableOnDebug implements TestRule {
047        private final TestRule rule;
048        private final boolean debugging;
049    
050        /**
051         * Create a {@code DisableOnDebug} instance with the timeout specified in
052         * milliseconds.
053         * 
054         * @param rule to disable during debugging
055         */
056        public DisableOnDebug(TestRule rule) {
057            this(rule, ManagementFactory.getRuntimeMXBean()
058                    .getInputArguments());
059        }
060    
061        /**
062         * Visible for testing purposes only.
063         * 
064         * @param rule the rule to disable during debugging
065         * @param inputArguments
066         *            arguments provided to the Java runtime
067         */
068        DisableOnDebug(TestRule rule, List<String> inputArguments) {
069            this.rule = rule;
070            debugging = isDebugging(inputArguments);
071        }
072    
073        /**
074         * @see TestRule#apply(Statement, Description)
075         */
076        public Statement apply(Statement base, Description description) {
077            if (debugging) {
078                return base;
079            } else {
080                return rule.apply(base, description);
081            }
082        }
083    
084        /**
085         * Parses arguments passed to the runtime environment for debug flags
086         * <p>
087         * Options specified in:
088         * <ul>
089         * <li>
090         * <a href="http://docs.oracle.com/javase/6/docs/technotes/guides/jpda/conninv.html#Invocation"
091         * >javase-6</a></li>
092         * <li><a href="http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/conninv.html#Invocation"
093         * >javase-7</a></li>
094         * <li><a href="http://docs.oracle.com/javase/8/docs/technotes/guides/jpda/conninv.html#Invocation"
095         * >javase-8</a></li>
096         * 
097         * 
098         * @param arguments
099         *            the arguments passed to the runtime environment, usually this
100         *            will be {@link RuntimeMXBean#getInputArguments()}
101         * @return true if the current JVM was started in debug mode, false
102         *         otherwise.
103         */
104        private static boolean isDebugging(List<String> arguments) {
105            for (final String argument : arguments) {
106                if ("-Xdebug".equals(argument)) {
107                    return true;
108                } else if (argument.startsWith("-agentlib:jdwp")) {
109                    return true;
110                }
111            }
112            return false;
113        }
114    
115        /**
116         * Returns {@code true} if the JVM is in debug mode. This method may be used
117         * by test classes to take additional action to disable code paths that
118         * interfere with debugging if required.
119         * 
120         * @return {@code true} if the current JVM is in debug mode, {@code false}
121         *         otherwise
122         */
123        public boolean isDebugging() {
124            return debugging;
125        }
126    
127    }