1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.lang3.time;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertNotEquals;
22 import static org.junit.jupiter.api.Assertions.assertNull;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25
26 import java.io.IOException;
27 import java.time.Duration;
28 import java.time.Instant;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.concurrent.TimeUnit;
32 import java.util.concurrent.atomic.AtomicInteger;
33
34 import org.apache.commons.lang3.AbstractLangTest;
35 import org.apache.commons.lang3.ThreadUtils;
36 import org.apache.commons.lang3.reflect.FieldUtils;
37 import org.junit.jupiter.api.RepeatedTest;
38 import org.junit.jupiter.api.Test;
39
40
41
42
43 class StopWatchTest extends AbstractLangTest {
44
45 private static final int SPLIT_CLOCK_STR_LEN = 12;
46 private static final Duration MIN_DURATION = Duration.ofMillis(20);
47 private static final String MESSAGE = "Baking cookies";
48 private static final String ZERO_HOURS_PREFIX = "00:";
49 private static final String ZERO_TIME_ELAPSED = "00:00:00.000";
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 private StopWatch createMockStopWatch(final long nanos) {
66 final StopWatch watch = StopWatch.createStarted();
67 watch.suspend();
68 return set(watch, nanos);
69 }
70
71 private StopWatch set(final StopWatch watch, final long nanos) {
72 try {
73 final long currentNanos = System.nanoTime();
74 final List<StopWatch.Split> splits = new ArrayList<>();
75 splits.add(new StopWatch.Split(String.valueOf(0), Duration.ofNanos(nanos)));
76 FieldUtils.writeField(watch, "startTimeNanos", currentNanos - nanos, true);
77 FieldUtils.writeField(watch, "stopTimeNanos", currentNanos, true);
78 FieldUtils.writeField(watch, "splits", splits, true);
79 } catch (final IllegalAccessException e) {
80 return null;
81 }
82 return watch;
83 }
84
85
86
87
88
89
90
91 private void sleepPlus1(final Duration duration) throws InterruptedException {
92 ThreadUtils.sleep(duration.plusMillis(1));
93 }
94
95
96
97
98 @Test
99 void testBadStates() {
100 final StopWatch watch = new StopWatch();
101 assertThrows(IllegalStateException.class, watch::stop, "Calling stop on an unstarted StopWatch should throw an exception.");
102 assertThrows(IllegalStateException.class, watch::suspend, "Calling suspend on an unstarted StopWatch should throw an exception.");
103 assertThrows(IllegalStateException.class, watch::split, "Calling split on a non-running StopWatch should throw an exception.");
104 assertThrows(IllegalStateException.class, watch::unsplit, "Calling unsplit on an unsplit StopWatch should throw an exception.");
105 assertThrows(IllegalStateException.class, watch::resume, "Calling resume on an unsuspended StopWatch should throw an exception.");
106 watch.start();
107 assertThrows(IllegalStateException.class, watch::start, "Calling start on a started StopWatch should throw an exception.");
108 assertThrows(IllegalStateException.class, watch::unsplit, "Calling unsplit on an unsplit StopWatch should throw an exception.");
109 assertThrows(IllegalStateException.class, watch::getSplitTime, "Calling getSplitTime on an unsplit StopWatch should throw an exception.");
110 assertThrows(IllegalStateException.class, watch::getSplitDuration, "Calling getSplitTime on an unsplit StopWatch should throw an exception.");
111 assertThrows(IllegalStateException.class, watch::resume, "Calling resume on an unsuspended StopWatch should throw an exception.");
112 watch.stop();
113 assertThrows(IllegalStateException.class, watch::start, "Calling start on a stopped StopWatch should throw an exception as it needs to be reset.");
114 }
115
116 @Test
117 void testBooleanStates() {
118 final StopWatch watch = new StopWatch();
119 assertFalse(watch.isStarted());
120 assertFalse(watch.isSuspended());
121 assertTrue(watch.isStopped());
122
123 watch.start();
124 assertTrue(watch.isStarted());
125 assertFalse(watch.isSuspended());
126 assertFalse(watch.isStopped());
127
128 watch.suspend();
129 assertTrue(watch.isStarted());
130 assertTrue(watch.isSuspended());
131 assertFalse(watch.isStopped());
132
133 watch.stop();
134 assertFalse(watch.isStarted());
135 assertFalse(watch.isSuspended());
136 assertTrue(watch.isStopped());
137 }
138
139 @Test
140 void testFormatSplitTime() {
141 final StopWatch watch = StopWatch.createStarted();
142 ThreadUtils.sleepQuietly(MIN_DURATION);
143 watch.split();
144 final String formatSplitTime = watch.formatSplitTime();
145 assertNotEquals(ZERO_TIME_ELAPSED, formatSplitTime);
146 assertTrue(formatSplitTime.startsWith(ZERO_HOURS_PREFIX), "formatSplitTime");
147 }
148
149 @Test
150 void testFormatSplitTimeWithMessage() {
151 final StopWatch watch = new StopWatch(MESSAGE);
152 watch.start();
153 ThreadUtils.sleepQuietly(MIN_DURATION);
154 watch.split();
155 final String formatSplitTime = watch.formatSplitTime();
156 assertFalse(formatSplitTime.startsWith(MESSAGE), "formatSplitTime");
157 assertTrue(formatSplitTime.startsWith(ZERO_HOURS_PREFIX), "formatSplitTime");
158 }
159
160 @Test
161 void testFormatTime() {
162 final StopWatch watch = StopWatch.create();
163 final String formatTime = watch.formatTime();
164 assertEquals(ZERO_TIME_ELAPSED, formatTime);
165 assertTrue(formatTime.startsWith(ZERO_HOURS_PREFIX), "formatTime");
166 }
167
168 @Test
169 void testFormatTimeWithMessage() {
170 final StopWatch watch = new StopWatch(MESSAGE);
171 final String formatTime = watch.formatTime();
172 assertFalse(formatTime.startsWith(MESSAGE), "formatTime");
173 }
174
175 @Test
176 void testGet() throws Throwable {
177 final StopWatch watch = new StopWatch();
178 final AtomicInteger i = new AtomicInteger();
179 assertEquals(1, watch.get(i::incrementAndGet));
180 assertEquals(2, watch.getT(i::incrementAndGet));
181 final IOException e = assertThrows(IOException.class, () -> watch.getT(this::throwIOException));
182 assertEquals("A", e.getMessage());
183
184 assertTrue(watch.isSuspended());
185 assertEquals(3, watch.get(() -> {
186 assertTrue(watch.isStarted());
187 return i.incrementAndGet();
188 }));
189 assertTrue(watch.isSuspended());
190 final long nanos1 = watch.getDuration().toNanos();
191 assertTrue(nanos1 >= 0);
192
193 assertTrue(watch.isSuspended());
194 assertEquals(4, watch.getT(() -> {
195 assertTrue(watch.isStarted());
196 return i.incrementAndGet();
197 }));
198 assertTrue(watch.isSuspended());
199 assertTrue(watch.getDuration().toNanos() >= nanos1);
200 }
201
202 @Test
203 void testGetDuration() throws InterruptedException {
204 final StopWatch watch = new StopWatch();
205 assertEquals(Duration.ZERO, watch.getDuration());
206 assertEquals(ZERO_TIME_ELAPSED, watch.toString());
207 watch.start();
208 sleepPlus1(MIN_DURATION);
209 final long nanos = watch.getNanoTime();
210 assertTrue(nanos > 0, () -> "getNanoTime(): " + nanos);
211 assertTrue(DurationUtils.isPositive(watch.getDuration()));
212 }
213
214 @Test
215 void testGetSplitDuration() {
216
217 final StopWatch watch = StopWatch.createStarted();
218 watch.split();
219 set(watch, 123456);
220 assertEquals(Duration.ofNanos(123456), watch.getSplitDuration());
221 }
222
223 @Test
224 void testGetSplits() {
225 final StopWatch stopWatch = StopWatch.create();
226 assertTrue(stopWatch.getSplits().isEmpty());
227 stopWatch.start();
228 testGetSplits(stopWatch);
229 testGetSplits(StopWatch.createStarted());
230 }
231
232 private void testGetSplits(final StopWatch watch) {
233 assertTrue(watch.getSplits().isEmpty());
234 watch.split();
235 assertEquals(1, watch.getSplits().size());
236 watch.unsplit();
237 assertTrue(watch.getSplits().isEmpty());
238 }
239
240 @Test
241 void testGetStartInstant() {
242 final long beforeStopWatchMillis = System.currentTimeMillis();
243 final StopWatch watch = new StopWatch();
244 assertThrows(IllegalStateException.class, watch::getStartInstant, "Calling getStartInstant on an unstarted StopWatch should throw an exception");
245 watch.start();
246
247 watch.getStartInstant();
248 assertTrue(watch.getStartInstant().compareTo(Instant.ofEpochMilli(beforeStopWatchMillis)) >= 0);
249
250 watch.reset();
251 assertThrows(IllegalStateException.class, watch::getStartInstant,
252 "Calling getStartInstant on a reset, but unstarted StopWatch should throw an exception");
253 }
254
255 @Test
256 void testGetStartTime() {
257 final long beforeStopWatchMillis = System.currentTimeMillis();
258 final StopWatch watch = new StopWatch();
259 assertThrows(IllegalStateException.class, watch::getStartTime, "Calling getStartTime on an unstarted StopWatch should throw an exception");
260 watch.start();
261
262 watch.getStartTime();
263 assertTrue(watch.getStartTime() >= beforeStopWatchMillis, "getStartTime");
264
265 watch.reset();
266 assertThrows(IllegalStateException.class, watch::getStartTime, "Calling getStartTime on a reset, but unstarted StopWatch should throw an exception");
267 }
268
269 @RepeatedTest(10)
270 void testGetTime() throws InterruptedException {
271 final StopWatch watch = new StopWatch();
272 assertEquals(0, watch.getTime());
273 assertEquals(ZERO_TIME_ELAPSED, watch.toString());
274 watch.start();
275 sleepPlus1(MIN_DURATION);
276 final long time = watch.getTime();
277 assertTrue(time > 0, () -> "getTime() millis: " + time);
278 assertTrue(time < 2000, () -> "getTime() millis: " + time);
279 }
280
281 @Test
282 void testGetWithTimeUnit() {
283
284
285 final StopWatch watch = createMockStopWatch(
286 TimeUnit.HOURS.toNanos(2)
287 + TimeUnit.MINUTES.toNanos(59)
288 + TimeUnit.SECONDS.toNanos(1)
289 + TimeUnit.MILLISECONDS.toNanos(999));
290
291 assertEquals(2L, watch.getTime(TimeUnit.HOURS));
292 assertEquals(179L, watch.getTime(TimeUnit.MINUTES));
293 assertEquals(10741L, watch.getTime(TimeUnit.SECONDS));
294 assertEquals(10741999L, watch.getTime(TimeUnit.MILLISECONDS));
295 }
296
297 @Test
298 void testLang315() throws InterruptedException {
299 final StopWatch watch = StopWatch.createStarted();
300 sleepPlus1(MIN_DURATION);
301 watch.suspend();
302 final long suspendTime = watch.getTime();
303 final Duration suspendDuration = watch.getDuration();
304 sleepPlus1(MIN_DURATION);
305 watch.stop();
306 final long totalTime = watch.getTime();
307 final Duration totalDuration = watch.getDuration();
308 assertEquals(suspendTime, totalTime);
309 assertEquals(suspendDuration, totalDuration);
310 }
311
312 @Test
313 void testMessage() {
314 assertNull(StopWatch.create().getMessage());
315 final StopWatch stopWatch = new StopWatch(MESSAGE);
316 assertEquals(MESSAGE, stopWatch.getMessage());
317 assertTrue(stopWatch.toString().startsWith(MESSAGE), "stopWatch.toString");
318 stopWatch.start();
319 stopWatch.split();
320 assertTrue(stopWatch.toSplitString().startsWith(MESSAGE), "stopWatch.toSplitString");
321 }
322
323 @Test
324 void testRun() throws Throwable {
325 final StopWatch watch = new StopWatch();
326 final AtomicInteger i = new AtomicInteger();
327 watch.run(i::incrementAndGet);
328 assertEquals(1, i.get());
329 watch.runT(i::incrementAndGet);
330 assertEquals(2, i.get());
331 final IOException e = assertThrows(IOException.class, () -> watch.runT(this::throwIOException));
332 assertEquals("A", e.getMessage());
333
334 assertTrue(watch.isSuspended());
335 watch.run(() -> {
336 assertTrue(watch.isStarted());
337 i.incrementAndGet();
338 });
339 assertEquals(3, i.get());
340 assertTrue(watch.isSuspended());
341 final long nanos1 = watch.getDuration().toNanos();
342 assertTrue(nanos1 > 0);
343
344 assertTrue(watch.isSuspended());
345 watch.runT(() -> {
346 assertTrue(watch.isStarted());
347 i.incrementAndGet();
348 });
349 assertEquals(4, i.get());
350 assertTrue(watch.isSuspended());
351 assertTrue(watch.getDuration().toNanos() >= nanos1);
352 }
353
354 @Test
355 void testSimple() throws InterruptedException {
356 final StopWatch watch = StopWatch.createStarted();
357 final Duration sleepDuration = MIN_DURATION;
358 sleepPlus1(sleepDuration);
359 watch.stop();
360 final long time = watch.getTime();
361 final Duration duration = watch.getDuration();
362 assertEquals(time, watch.getTime());
363 assertEquals(duration, watch.getDuration());
364 assertTrue(duration.compareTo(sleepDuration) >= 0, () -> "duration: " + duration);
365 watch.reset();
366 assertEquals(0, watch.getTime());
367 assertEquals(Duration.ZERO, watch.getDuration());
368 }
369
370 @Test
371 void testSplit() throws InterruptedException {
372 final StopWatch watch = StopWatch.createStarted();
373 final Duration sleepDuration = MIN_DURATION;
374 final long sleepMillis = sleepDuration.toMillis();
375 assertTrue(sleepMillis > 0);
376 sleepPlus1(sleepDuration);
377 watch.split();
378 final long splitTime = watch.getSplitTime();
379 final Duration splitDuration = watch.getSplitDuration();
380 assertEquals(splitTime, watch.getSplitDuration().toMillis());
381 assertEquals(SPLIT_CLOCK_STR_LEN, watch.toSplitString().length(), "Formatted split string not the correct length");
382 sleepPlus1(sleepDuration);
383 watch.unsplit();
384 sleepPlus1(sleepDuration);
385 watch.stop();
386 final long totalTime = watch.getTime();
387 final Duration totalDuration = watch.getDuration();
388 assertTrue(splitTime >= sleepMillis, () -> "splitTime: " + splitTime);
389 assertTrue(splitDuration.toMillis() >= sleepMillis, () -> "splitDuration: " + splitDuration);
390 final long sleepMillisX3 = sleepMillis * 3;
391 assertTrue(totalTime >= sleepMillisX3 && splitTime < 21000);
392 assertTrue(totalDuration.toMillis() >= sleepMillisX3);
393 }
394
395 @Test
396 void testStatic() {
397 final StopWatch watch = StopWatch.createStarted();
398 assertTrue(watch.isStarted());
399 }
400
401 @Test
402 void testStopInstantSimple() throws InterruptedException {
403 final StopWatch watch = StopWatch.createStarted();
404 final long testStartMillis = System.currentTimeMillis();
405 sleepPlus1(MIN_DURATION);
406 watch.stop();
407 final long testEndMillis = System.currentTimeMillis();
408 final Instant stopTime = watch.getStopInstant();
409 assertEquals(stopTime, watch.getStopInstant());
410
411 assertTrue(testStartMillis < testEndMillis);
412 assertTrue(Instant.ofEpochMilli(testStartMillis).isBefore(Instant.ofEpochMilli(testEndMillis)));
413 }
414
415 @Test
416 void testStopTimeSimple() throws InterruptedException {
417 final StopWatch watch = StopWatch.createStarted();
418 final long testStartMillis = System.currentTimeMillis();
419 sleepPlus1(MIN_DURATION);
420 watch.stop();
421 final long testEndMillis = System.currentTimeMillis();
422 final long stopTime = watch.getStopTime();
423 assertEquals(stopTime, watch.getStopTime());
424
425 assertTrue(testStartMillis < testEndMillis);
426 }
427
428 @Test
429 void testSuspend() throws InterruptedException {
430
431 final StopWatch watch = StopWatch.createStarted();
432 final long testStartMillis = System.currentTimeMillis();
433 final long testStartNanos = System.nanoTime();
434 final Instant testStartInstant = Instant.ofEpochMilli(testStartMillis);
435 final Duration sleepDuration = MIN_DURATION;
436 final long sleepMillis = sleepDuration.toMillis();
437 sleepPlus1(sleepDuration);
438 watch.suspend();
439 final long testSuspendMillis = System.currentTimeMillis();
440 final long testSuspendNanos = System.nanoTime();
441 final long testSuspendTimeNanos = testSuspendNanos - testStartNanos;
442
443 final Duration testSuspendDuration = Duration.ofNanos(testSuspendTimeNanos).plusMillis(1);
444 final long suspendTimeFromNanos = watch.getTime();
445 final Duration suspendDuration = watch.getDuration();
446 final long stopTimeMillis = watch.getStopTime();
447 final Instant stopInstant = watch.getStopInstant();
448
449 assertTrue(testStartMillis <= stopTimeMillis, () -> String.format("testStartMillis %s <= stopTimeMillis %s", testStartMillis, stopTimeMillis));
450 assertTrue(testStartInstant.isBefore(stopInstant), () -> String.format("testStartInstant %s < stopInstant %s", testStartInstant, stopInstant));
451 assertTrue(testSuspendMillis <= stopTimeMillis, () -> String.format("testSuspendMillis %s <= stopTimeMillis %s", testSuspendMillis, stopTimeMillis));
452 assertTrue(testSuspendMillis <= stopInstant.toEpochMilli(),
453 () -> String.format("testSuspendMillis %s <= stopInstant %s", testSuspendMillis, stopInstant));
454
455 sleepPlus1(sleepDuration);
456 watch.resume();
457 sleepPlus1(sleepDuration);
458 watch.stop();
459 final long totalTimeFromNanos = watch.getTime();
460 final Duration totalDuration = watch.getDuration();
461
462 assertTrue(suspendTimeFromNanos >= sleepMillis, () -> String.format("suspendTimeFromNanos %s >= sleepMillis %s", suspendTimeFromNanos, sleepMillis));
463 assertTrue(suspendDuration.compareTo(Duration.ofMillis(sleepMillis)) >= 0,
464 () -> String.format("suspendDuration %s >= sleepMillis %s", suspendDuration, sleepMillis));
465 assertTrue(suspendTimeFromNanos <= testSuspendTimeNanos,
466 () -> String.format("suspendTimeFromNanos %s <= testSuspendTimeNanos %s", suspendTimeFromNanos, testSuspendTimeNanos));
467 assertTrue(suspendDuration.compareTo(testSuspendDuration) <= 0,
468 () -> String.format("suspendDuration %s <= testSuspendDuration %s", suspendDuration, testSuspendDuration));
469
470 final long sleepMillisX2 = sleepMillis + sleepMillis;
471 assertTrue(totalTimeFromNanos >= sleepMillisX2, () -> String.format("totalTimeFromNanos %s >= sleepMillisX2 %s", totalTimeFromNanos, sleepMillisX2));
472 assertTrue(totalDuration.compareTo(Duration.ofMillis(sleepMillisX2)) >= 0,
473 () -> String.format("totalDuration >= sleepMillisX2", totalDuration, sleepMillisX2));
474
475
476 final long testTooLongMillis = sleepMillis * 100;
477 assertTrue(totalTimeFromNanos < testTooLongMillis,
478 () -> String.format("totalTimeFromNanos %s < testTooLongMillis %s", totalTimeFromNanos, testTooLongMillis));
479 assertTrue(totalDuration.compareTo(Duration.ofMillis(testTooLongMillis)) < 0,
480 () -> String.format("totalDuration %s < testTooLongMillis %s", totalDuration, testTooLongMillis));
481
482 }
483
484 @Test
485 void testToSplitString() throws InterruptedException {
486 final StopWatch watch = StopWatch.createStarted();
487 sleepPlus1(MIN_DURATION);
488 watch.split();
489 final String splitStr = watch.toSplitString();
490 assertEquals(SPLIT_CLOCK_STR_LEN, splitStr.length(), "Formatted split string not the correct length");
491 }
492
493 @Test
494 void testToSplitStringWithMessage() throws InterruptedException {
495 final StopWatch watch = new StopWatch(MESSAGE);
496 watch.start();
497 sleepPlus1(MIN_DURATION);
498 watch.split();
499 final String splitStr = watch.toSplitString();
500 assertEquals(SPLIT_CLOCK_STR_LEN + MESSAGE.length() + 1, splitStr.length(), "Formatted split string not the correct length");
501 }
502
503 @Test
504 void testToString() throws InterruptedException {
505
506 final StopWatch watch = StopWatch.createStarted();
507 sleepPlus1(MIN_DURATION);
508 watch.split();
509 final String splitStr = watch.toString();
510 assertEquals(SPLIT_CLOCK_STR_LEN, splitStr.length(), "Formatted split string not the correct length");
511 }
512
513 @Test
514 void testToStringWithMessage() throws InterruptedException {
515 assertTrue(new StopWatch(MESSAGE).toString().startsWith(MESSAGE), "message");
516
517 final StopWatch watch = new StopWatch(MESSAGE);
518 watch.start();
519 sleepPlus1(MIN_DURATION);
520 watch.split();
521 final String splitStr = watch.toString();
522 assertEquals(SPLIT_CLOCK_STR_LEN + MESSAGE.length() + 1, splitStr.length(), "Formatted split string not the correct length");
523 }
524
525 @Test
526 void testSplitsWithStringLabels() {
527 final StopWatch watch = new StopWatch();
528 final String firstLabel = "one";
529 final String secondLabel = "two";
530 final String thirdLabel = "three";
531 watch.start();
532
533 watch.split(firstLabel);
534 watch.split(secondLabel);
535 watch.split(thirdLabel);
536 watch.stop();
537
538 final List<StopWatch.Split> splits = watch.getSplits();
539
540 assertEquals(3, splits.size());
541
542 assertEquals(firstLabel, splits.get(0).getLabel());
543 assertEquals(secondLabel, splits.get(1).getLabel());
544 assertEquals(thirdLabel, splits.get(2).getLabel());
545
546 assertTrue(splits.get(0).getDuration().toNanos() > 0);
547 assertTrue(splits.get(1).getDuration().toNanos() > 0);
548 assertTrue(splits.get(2).getDuration().toNanos() > 0);
549
550 watch.unsplit();
551 assertEquals(2, watch.getSplits().size());
552 assertThrows(IllegalStateException.class, watch::unsplit);
553 }
554
555 private int throwIOException() throws IOException {
556 throw new IOException("A");
557 }
558 }