Skip to content

Commit 4914d90

Browse files
committed
Merge pull request #937 from kcooney/test-watcher-delegation
Change Stopwatch to delegate to TestWatcher. Fix Stopwatch.runtime()
2 parents 2f86c6f + 0ffc637 commit 4914d90

File tree

4 files changed

+280
-222
lines changed

4 files changed

+280
-222
lines changed

src/main/java/org/junit/rules/Stopwatch.java

+66-58
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.junit.AssumptionViolatedException;
44
import org.junit.runner.Description;
5+
import org.junit.runners.model.Statement;
56

67
import java.util.concurrent.TimeUnit;
78

@@ -13,33 +14,34 @@
1314
*
1415
* <pre>
1516
* public static class StopwatchTest {
16-
* private static final Logger logger= Logger.getLogger(&quot;&quot;);
17+
* private static final Logger logger = Logger.getLogger(&quot;&quot;);
1718
*
18-
* private static void logInfo(String testName, String status, long nanos) {
19+
* private static void logInfo(Description description, String status, long nanos) {
20+
* String testName = description.getMethodName();
1921
* logger.info(String.format(&quot;Test %s %s, spent %d microseconds&quot;,
20-
* testName, status, Stopwatch.toMicros(nanos)));
22+
* testName, status, TimeUnit.NANOSECONDS.toMicros(nanos)));
2123
* }
2224
*
2325
* &#064;Rule
24-
* public Stopwatch stopwatch= new Stopwatch() {
26+
* public Stopwatch stopwatch = new Stopwatch() {
2527
* &#064;Override
2628
* protected void succeeded(long nanos, Description description) {
27-
* logInfo(description.getMethodName(), &quot;succeeded&quot;, nanos);
29+
* logInfo(description, &quot;succeeded&quot;, nanos);
2830
* }
2931
*
3032
* &#064;Override
3133
* protected void failed(long nanos, Throwable e, Description description) {
32-
* logInfo(description.getMethodName(), &quot;failed&quot;, nanos);
34+
* logInfo(description, &quot;failed&quot;, nanos);
3335
* }
3436
*
3537
* &#064;Override
3638
* protected void skipped(long nanos, AssumptionViolatedException e, Description description) {
37-
* logInfo(description.getMethodName(), &quot;skipped&quot;, nanos);
39+
* logInfo(description, &quot;skipped&quot;, nanos);
3840
* }
3941
*
4042
* &#064;Override
4143
* protected void finished(long nanos, Description description) {
42-
* logInfo(description.getMethodName(), &quot;finished&quot;, nanos);
44+
* logInfo(description, &quot;finished&quot;, nanos);
4345
* }
4446
* };
4547
*
@@ -63,7 +65,7 @@
6365
* <pre>
6466
* &#064;Test
6567
* public void performanceTest() throws InterruptedException {
66-
* long delta= 30;
68+
* long delta = 30;
6769
* Thread.sleep(300L);
6870
* assertEquals(300d, stopwatch.runtime(MILLISECONDS), delta);
6971
* Thread.sleep(500L);
@@ -74,16 +76,27 @@
7476
* @author tibor17
7577
* @since 4.12
7678
*/
77-
public class Stopwatch extends TestWatcher {
78-
private long startNanos;
79-
private long endNanos;
79+
public abstract class Stopwatch implements TestRule {
80+
private final Clock clock;
81+
private volatile long startNanos;
82+
private volatile long endNanos;
83+
84+
public Stopwatch() {
85+
this(new Clock());
86+
}
87+
88+
Stopwatch(Clock clock) {
89+
this.clock = clock;
90+
}
8091

8192
/**
93+
* Gets the runtime for the test.
94+
*
8295
* @param unit time unit for returned runtime
8396
* @return runtime measured during the test
8497
*/
8598
public long runtime(TimeUnit unit) {
86-
return unit.convert(currentNanoTime() - startNanos, TimeUnit.NANOSECONDS);
99+
return unit.convert(getNanos(), TimeUnit.NANOSECONDS);
87100
}
88101

89102
/**
@@ -110,66 +123,61 @@ protected void skipped(long nanos, AssumptionViolatedException e, Description de
110123
protected void finished(long nanos, Description description) {
111124
}
112125

113-
/**
114-
* @param nanos time in nanoseconds
115-
* @return time converted to microseconds
116-
*/
117-
public static long toMicros(long nanos) {
118-
return TimeUnit.NANOSECONDS.toMicros(nanos);
119-
}
120-
121-
/**
122-
* @param nanos time in nanoseconds
123-
* @return time converted to milliseconds
124-
*/
125-
public static long toMillis(long nanos) {
126-
return TimeUnit.NANOSECONDS.toMillis(nanos);
127-
}
128-
129-
/**
130-
* @param nanos time in nanoseconds
131-
* @return time converted to seconds
132-
*/
133-
public static long toSeconds(long nanos) {
134-
return TimeUnit.NANOSECONDS.toSeconds(nanos);
135-
}
136-
137126
private long getNanos() {
138-
return endNanos - startNanos;
127+
if (startNanos == 0) {
128+
throw new IllegalStateException("Test has not started");
129+
}
130+
long currentEndNanos = endNanos; // volatile read happens here
131+
if (currentEndNanos == 0) {
132+
currentEndNanos = clock.nanoTime();
133+
}
134+
135+
return currentEndNanos - startNanos;
139136
}
140137

141138
private void starting() {
142-
startNanos = currentNanoTime();
139+
startNanos = clock.nanoTime();
140+
endNanos = 0;
143141
}
144142

145143
private void stopping() {
146-
endNanos = currentNanoTime();
144+
endNanos = clock.nanoTime();
147145
}
148146

149-
private long currentNanoTime() {
150-
return System.nanoTime();
147+
public final Statement apply(Statement base, Description description) {
148+
return new InternalWatcher().apply(base, description);
151149
}
152150

153-
@Override final protected void succeeded(Description description) {
154-
stopping();
155-
succeeded(getNanos(), description);
156-
}
151+
private class InternalWatcher extends TestWatcher {
157152

158-
@Override final protected void failed(Throwable e, Description description) {
159-
stopping();
160-
failed(getNanos(), e, description);
161-
}
153+
@Override protected void starting(Description description) {
154+
Stopwatch.this.starting();
155+
}
162156

163-
@Override final protected void skipped(AssumptionViolatedException e, Description description) {
164-
stopping();
165-
skipped(getNanos(), e, description);
166-
}
157+
@Override protected void finished(Description description) {
158+
Stopwatch.this.finished(getNanos(), description);
159+
}
160+
161+
@Override protected void succeeded(Description description) {
162+
Stopwatch.this.stopping();
163+
Stopwatch.this.succeeded(getNanos(), description);
164+
}
167165

168-
@Override final protected void starting(Description description) {
169-
starting();
166+
@Override protected void failed(Throwable e, Description description) {
167+
Stopwatch.this.stopping();
168+
Stopwatch.this.failed(getNanos(), e, description);
169+
}
170+
171+
@Override protected void skipped(AssumptionViolatedException e, Description description) {
172+
Stopwatch.this.stopping();
173+
Stopwatch.this.skipped(getNanos(), e, description);
174+
}
170175
}
171176

172-
@Override final protected void finished(Description description) {
173-
finished(getNanos(), description);
177+
static class Clock {
178+
179+
public long nanoTime() {
180+
return System.nanoTime();
181+
}
174182
}
175183
}

0 commit comments

Comments
 (0)