001package org.junit.rules;
002
003import java.lang.management.ManagementFactory;
004import java.lang.management.RuntimeMXBean;
005import java.util.List;
006
007import org.junit.runner.Description;
008import 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 */
046public 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}