| 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 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(); |
| } |
| } |
| } |