diff options
Diffstat (limited to 'src/main/java/org/junit/rules/Stopwatch.java')
-rw-r--r-- | src/main/java/org/junit/rules/Stopwatch.java | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/main/java/org/junit/rules/Stopwatch.java b/src/main/java/org/junit/rules/Stopwatch.java new file mode 100644 index 0000000..5d34e7f --- /dev/null +++ b/src/main/java/org/junit/rules/Stopwatch.java @@ -0,0 +1,183 @@ +package org.junit.rules; + +import org.junit.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +import java.util.concurrent.TimeUnit; + +/** + * The Stopwatch Rule notifies one of its own protected methods of the time spent by a test. + * + * <p>Override them to get the time in nanoseconds. For example, this class will keep logging the + * time spent by each passed, failed, skipped, and finished test: + * + * <pre> + * public static class StopwatchTest { + * private static final Logger logger = Logger.getLogger(""); + * + * private static void logInfo(Description description, String status, long nanos) { + * String testName = description.getMethodName(); + * logger.info(String.format("Test %s %s, spent %d microseconds", + * testName, status, TimeUnit.NANOSECONDS.toMicros(nanos))); + * } + * + * @Rule + * public Stopwatch stopwatch = new Stopwatch() { + * @Override + * protected void succeeded(long nanos, Description description) { + * logInfo(description, "succeeded", nanos); + * } + * + * @Override + * protected void failed(long nanos, Throwable e, Description description) { + * logInfo(description, "failed", nanos); + * } + * + * @Override + * protected void skipped(long nanos, AssumptionViolatedException e, Description description) { + * logInfo(description, "skipped", nanos); + * } + * + * @Override + * protected void finished(long nanos, Description description) { + * logInfo(description, "finished", nanos); + * } + * }; + * + * @Test + * public void succeeds() { + * } + * + * @Test + * public void fails() { + * fail(); + * } + * + * @Test + * public void skips() { + * assumeTrue(false); + * } + * } + * </pre> + * + * An example to assert runtime: + * <pre> + * @Test + * public void performanceTest() throws InterruptedException { + * long delta = 30; + * Thread.sleep(300L); + * assertEquals(300d, stopwatch.runtime(MILLISECONDS), delta); + * Thread.sleep(500L); + * assertEquals(800d, stopwatch.runtime(MILLISECONDS), delta); + * } + * </pre> + * + * @author tibor17 + * @since 4.12 + */ +public abstract class Stopwatch implements TestRule { + private final Clock clock; + private volatile long startNanos; + private volatile long endNanos; + + public Stopwatch() { + this(new Clock()); + } + + Stopwatch(Clock clock) { + this.clock = clock; + } + + /** + * Gets the runtime for the test. + * + * @param unit time unit for returned runtime + * @return runtime measured during the test + */ + public long runtime(TimeUnit unit) { + return unit.convert(getNanos(), TimeUnit.NANOSECONDS); + } + + /** + * Invoked when a test succeeds + */ + protected void succeeded(long nanos, Description description) { + } + + /** + * Invoked when a test fails + */ + protected void failed(long nanos, Throwable e, Description description) { + } + + /** + * Invoked when a test is skipped due to a failed assumption. + */ + protected void skipped(long nanos, AssumptionViolatedException e, Description description) { + } + + /** + * Invoked when a test method finishes (whether passing or failing) + */ + protected void finished(long nanos, Description description) { + } + + private long getNanos() { + if (startNanos == 0) { + throw new IllegalStateException("Test has not started"); + } + long currentEndNanos = endNanos; // volatile read happens here + if (currentEndNanos == 0) { + currentEndNanos = clock.nanoTime(); + } + + return currentEndNanos - startNanos; + } + + private void starting() { + startNanos = clock.nanoTime(); + endNanos = 0; + } + + private void stopping() { + endNanos = clock.nanoTime(); + } + + public final Statement apply(Statement base, Description description) { + return new InternalWatcher().apply(base, description); + } + + private class InternalWatcher extends TestWatcher { + + @Override protected void starting(Description description) { + Stopwatch.this.starting(); + } + + @Override protected void finished(Description description) { + Stopwatch.this.finished(getNanos(), description); + } + + @Override protected void succeeded(Description description) { + Stopwatch.this.stopping(); + Stopwatch.this.succeeded(getNanos(), description); + } + + @Override protected void failed(Throwable e, Description description) { + Stopwatch.this.stopping(); + Stopwatch.this.failed(getNanos(), e, description); + } + + @Override protected void skipped(AssumptionViolatedException e, Description description) { + Stopwatch.this.stopping(); + Stopwatch.this.skipped(getNanos(), e, description); + } + } + + static class Clock { + + public long nanoTime() { + return System.nanoTime(); + } + } +} |