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