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