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 nanos) {
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(nanos)));
77 FieldUtils.writeField(watch, "startTimeNanos", currentNanos - nanos, 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 testFormatSplitTimeWithMessage() {
152 final StopWatch watch = new StopWatch(MESSAGE);
153 watch.start();
154 ThreadUtils.sleepQuietly(MIN_DURATION);
155 watch.split();
156 final String formatSplitTime = watch.formatSplitTime();
157 assertFalse(formatSplitTime.startsWith(MESSAGE), "formatSplitTime");
158 assertTrue(formatSplitTime.startsWith(ZERO_HOURS_PREFIX), "formatSplitTime");
159 }
160
161 @Test
162 void testFormatTime() {
163 final StopWatch watch = StopWatch.create();
164 final String formatTime = watch.formatTime();
165 assertEquals(ZERO_TIME_ELAPSED, formatTime);
166 assertTrue(formatTime.startsWith(ZERO_HOURS_PREFIX), "formatTime");
167 }
168
169 @Test
170 void testFormatTimeWithMessage() {
171 final StopWatch watch = new StopWatch(MESSAGE);
172 final String formatTime = watch.formatTime();
173 assertFalse(formatTime.startsWith(MESSAGE), "formatTime");
174 }
175
176 @Test
177 void testGet() throws Throwable {
178 final StopWatch watch = new StopWatch();
179 final AtomicInteger i = new AtomicInteger();
180 assertEquals(1, watch.get(i::incrementAndGet));
181 assertEquals(2, watch.getT(i::incrementAndGet));
182 final IOException e = assertThrows(IOException.class, () -> watch.getT(this::throwIOException));
183 assertEquals("A", e.getMessage());
184
185 assertTrue(watch.isSuspended());
186 assertEquals(3, watch.get(() -> {
187 assertTrue(watch.isStarted());
188 return i.incrementAndGet();
189 }));
190 assertTrue(watch.isSuspended());
191 final long nanos1 = watch.getDuration().toNanos();
192 assertTrue(nanos1 >= 0);
193
194 assertTrue(watch.isSuspended());
195 assertEquals(4, watch.getT(() -> {
196 assertTrue(watch.isStarted());
197 return i.incrementAndGet();
198 }));
199 assertTrue(watch.isSuspended());
200 assertTrue(watch.getDuration().toNanos() >= nanos1);
201 }
202
203 @Test
204 void testGetDuration() throws InterruptedException {
205 final StopWatch watch = new StopWatch();
206 assertEquals(Duration.ZERO, watch.getDuration());
207 assertEquals(ZERO_TIME_ELAPSED, watch.toString());
208 watch.start();
209 sleepPlus1(MIN_DURATION);
210 final long nanos = watch.getNanoTime();
211 assertTrue(nanos > 0, () -> "getNanoTime(): " + nanos);
212 assertTrue(DurationUtils.isPositive(watch.getDuration()));
213 }
214
215 @Test
216 void testGetSplitDuration() {
217
218 final StopWatch watch = StopWatch.createStarted();
219 watch.split();
220 set(watch, 123456);
221 assertEquals(Duration.ofNanos(123456), watch.getSplitDuration());
222 }
223
224 @Test
225 void testGetSplits() {
226 final StopWatch stopWatch = StopWatch.create();
227 assertTrue(stopWatch.getSplits().isEmpty());
228 stopWatch.start();
229 testGetSplits(stopWatch);
230 testGetSplits(StopWatch.createStarted());
231 }
232
233 private void testGetSplits(final StopWatch watch) {
234 assertTrue(watch.getSplits().isEmpty());
235 watch.split();
236 assertEquals(1, watch.getSplits().size());
237 watch.unsplit();
238 assertTrue(watch.getSplits().isEmpty());
239 }
240
241 @Test
242 void testGetStartInstant() {
243 final long beforeStopWatchMillis = System.currentTimeMillis();
244 final StopWatch watch = new StopWatch();
245 assertThrows(IllegalStateException.class, watch::getStartInstant, "Calling getStartInstant on an unstarted StopWatch should throw an exception");
246 watch.start();
247
248 watch.getStartInstant();
249 assertTrue(watch.getStartInstant().compareTo(Instant.ofEpochMilli(beforeStopWatchMillis)) >= 0);
250
251 watch.reset();
252 assertThrows(IllegalStateException.class, watch::getStartInstant,
253 "Calling getStartInstant on a reset, but unstarted StopWatch should throw an exception");
254 }
255
256 @Test
257 void testGetStartTime() {
258 final long beforeStopWatchMillis = System.currentTimeMillis();
259 final StopWatch watch = new StopWatch();
260 assertThrows(IllegalStateException.class, watch::getStartTime, "Calling getStartTime on an unstarted StopWatch should throw an exception");
261 watch.start();
262
263 watch.getStartTime();
264 assertTrue(watch.getStartTime() >= beforeStopWatchMillis, "getStartTime");
265
266 watch.reset();
267 assertThrows(IllegalStateException.class, watch::getStartTime, "Calling getStartTime on a reset, but unstarted StopWatch should throw an exception");
268 }
269
270 @RepeatedTest(10)
271 void testGetTime() throws InterruptedException {
272 final StopWatch watch = new StopWatch();
273 assertEquals(0, watch.getTime());
274 assertEquals(ZERO_TIME_ELAPSED, watch.toString());
275 watch.start();
276 sleepPlus1(MIN_DURATION);
277 final long time = watch.getTime();
278 assertTrue(time > 0, () -> "getTime() millis: " + time);
279 assertTrue(time < 2000, () -> "getTime() millis: " + time);
280 }
281
282 @Test
283 void testGetWithTimeUnit() {
284
285
286 final StopWatch watch = createMockStopWatch(
287 TimeUnit.HOURS.toNanos(2)
288 + TimeUnit.MINUTES.toNanos(59)
289 + TimeUnit.SECONDS.toNanos(1)
290 + TimeUnit.MILLISECONDS.toNanos(999));
291
292 assertEquals(2L, watch.getTime(TimeUnit.HOURS));
293 assertEquals(179L, watch.getTime(TimeUnit.MINUTES));
294 assertEquals(10741L, watch.getTime(TimeUnit.SECONDS));
295 assertEquals(10741999L, watch.getTime(TimeUnit.MILLISECONDS));
296 }
297
298 @Test
299 void testLang315() throws InterruptedException {
300 final StopWatch watch = StopWatch.createStarted();
301 sleepPlus1(MIN_DURATION);
302 watch.suspend();
303 final long suspendTime = watch.getTime();
304 final Duration suspendDuration = watch.getDuration();
305 sleepPlus1(MIN_DURATION);
306 watch.stop();
307 final long totalTime = watch.getTime();
308 final Duration totalDuration = watch.getDuration();
309 assertEquals(suspendTime, totalTime);
310 assertEquals(suspendDuration, totalDuration);
311 }
312
313 @Test
314 void testMessage() {
315 assertNull(StopWatch.create().getMessage());
316 final StopWatch stopWatch = new StopWatch(MESSAGE);
317 assertEquals(MESSAGE, stopWatch.getMessage());
318 assertTrue(stopWatch.toString().startsWith(MESSAGE), "stopWatch.toString");
319 stopWatch.start();
320 stopWatch.split();
321 assertTrue(stopWatch.toSplitString().startsWith(MESSAGE), "stopWatch.toSplitString");
322 }
323
324 @Test
325 void testRun() throws Throwable {
326 final StopWatch watch = new StopWatch();
327 final AtomicInteger i = new AtomicInteger();
328 watch.run(i::incrementAndGet);
329 assertEquals(1, i.get());
330 watch.runT(i::incrementAndGet);
331 assertEquals(2, i.get());
332 final IOException e = assertThrows(IOException.class, () -> watch.runT(this::throwIOException));
333 assertEquals("A", e.getMessage());
334
335 assertTrue(watch.isSuspended());
336 watch.run(() -> {
337 assertTrue(watch.isStarted());
338 i.incrementAndGet();
339 });
340 assertEquals(3, i.get());
341 assertTrue(watch.isSuspended());
342 final long nanos1 = watch.getDuration().toNanos();
343 assertTrue(nanos1 > 0);
344
345 assertTrue(watch.isSuspended());
346 watch.runT(() -> {
347 assertTrue(watch.isStarted());
348 i.incrementAndGet();
349 });
350 assertEquals(4, i.get());
351 assertTrue(watch.isSuspended());
352 assertTrue(watch.getDuration().toNanos() >= nanos1);
353 }
354
355 @Test
356 void testSimple() throws InterruptedException {
357 final StopWatch watch = StopWatch.createStarted();
358 final Duration sleepDuration = MIN_DURATION;
359 sleepPlus1(sleepDuration);
360 watch.stop();
361 final long time = watch.getTime();
362 final Duration duration = watch.getDuration();
363 assertEquals(time, watch.getTime());
364 assertEquals(duration, watch.getDuration());
365 assertTrue(duration.compareTo(sleepDuration) >= 0, () -> "duration: " + duration);
366 watch.reset();
367 assertEquals(0, watch.getTime());
368 assertEquals(Duration.ZERO, watch.getDuration());
369 }
370
371 @Test
372 void testSplit() throws InterruptedException {
373 final StopWatch watch = StopWatch.createStarted();
374 final Duration sleepDuration = MIN_DURATION;
375 final long sleepMillis = sleepDuration.toMillis();
376 assertTrue(sleepMillis > 0);
377 sleepPlus1(sleepDuration);
378 watch.split();
379 final long splitTime = watch.getSplitTime();
380 final Duration splitDuration = watch.getSplitDuration();
381 assertEquals(splitTime, watch.getSplitDuration().toMillis());
382 assertEquals(SPLIT_CLOCK_STR_LEN, watch.toSplitString().length(), "Formatted split string not the correct length");
383 sleepPlus1(sleepDuration);
384 watch.unsplit();
385 sleepPlus1(sleepDuration);
386 watch.stop();
387 final long totalTime = watch.getTime();
388 final Duration totalDuration = watch.getDuration();
389 assertTrue(splitTime >= sleepMillis, () -> "splitTime: " + splitTime);
390 assertTrue(splitDuration.toMillis() >= sleepMillis, () -> "splitDuration: " + splitDuration);
391 final long sleepMillisX3 = sleepMillis * 3;
392 assertTrue(totalTime >= sleepMillisX3 && splitTime < 21000);
393 assertTrue(totalDuration.toMillis() >= sleepMillisX3);
394 }
395
396 @Test
397 void testSplitGetStopInstant() {
398 final StopWatch watch = StopWatch.createStarted();
399 watch.split();
400 assertNotNull(watch.getStopTime());
401 assertNotNull(watch.getStopInstant());
402 }
403
404 @Test
405 void testSplitsWithStringLabels() {
406 final StopWatch watch = new StopWatch();
407 final String firstLabel = "one";
408 final String secondLabel = "two";
409 final String thirdLabel = "three";
410 watch.start();
411
412 watch.split(firstLabel);
413 watch.split(secondLabel);
414 watch.split(thirdLabel);
415 watch.stop();
416
417 final List<StopWatch.Split> splits = watch.getSplits();
418
419 assertEquals(3, splits.size());
420
421 assertEquals(firstLabel, splits.get(0).getLabel());
422 assertEquals(secondLabel, splits.get(1).getLabel());
423 assertEquals(thirdLabel, splits.get(2).getLabel());
424
425 assertTrue(splits.get(0).getDuration().toNanos() > 0);
426 assertTrue(splits.get(1).getDuration().toNanos() > 0);
427 assertTrue(splits.get(2).getDuration().toNanos() > 0);
428
429 watch.unsplit();
430 assertEquals(2, watch.getSplits().size());
431 assertThrows(IllegalStateException.class, watch::unsplit);
432 }
433
434 @Test
435 void testSplitWithLabelGetStopInstant() {
436 final StopWatch watch = StopWatch.createStarted();
437 watch.split("one");
438 assertNotNull(watch.getStopTime());
439 assertNotNull(watch.getStopInstant());
440 }
441
442 @Test
443 void testStatic() {
444 final StopWatch watch = StopWatch.createStarted();
445 assertTrue(watch.isStarted());
446 }
447
448 @Test
449 void testGetStopTime() throws InterruptedException {
450 final StopWatch watch = StopWatch.createStarted();
451 assertEquals(0, watch.getStopTime());
452 }
453
454 @Test
455 void testStopInstantSimple() throws InterruptedException {
456 final StopWatch watch = StopWatch.createStarted();
457 final long testStartMillis = System.currentTimeMillis();
458 sleepPlus1(MIN_DURATION);
459 watch.stop();
460 final long testEndMillis = System.currentTimeMillis();
461 final Instant stopTime = watch.getStopInstant();
462 assertEquals(stopTime, watch.getStopInstant());
463
464 assertTrue(testStartMillis < testEndMillis);
465 assertTrue(Instant.ofEpochMilli(testStartMillis).isBefore(Instant.ofEpochMilli(testEndMillis)));
466 }
467
468 @Test
469 void testStopTimeSimple() throws InterruptedException {
470 final StopWatch watch = StopWatch.createStarted();
471 final long testStartMillis = System.currentTimeMillis();
472 sleepPlus1(MIN_DURATION);
473 watch.stop();
474 final long testEndMillis = System.currentTimeMillis();
475 final long stopTime = watch.getStopTime();
476 assertEquals(stopTime, watch.getStopTime());
477
478 assertTrue(testStartMillis < testEndMillis);
479 }
480
481 @Test
482 void testSuspend() throws InterruptedException {
483
484 final StopWatch watch = StopWatch.createStarted();
485 final long testStartMillis = System.currentTimeMillis();
486 final long testStartNanos = System.nanoTime();
487 final Instant testStartInstant = Instant.ofEpochMilli(testStartMillis);
488 final Duration sleepDuration = MIN_DURATION;
489 final long sleepMillis = sleepDuration.toMillis();
490 sleepPlus1(sleepDuration);
491 watch.suspend();
492 final long testSuspendMillis = System.currentTimeMillis();
493 final long testSuspendNanos = System.nanoTime();
494 final long testSuspendTimeNanos = testSuspendNanos - testStartNanos;
495
496 final Duration testSuspendDuration = Duration.ofNanos(testSuspendTimeNanos).plusMillis(1);
497 final long suspendTimeFromNanos = watch.getTime();
498 final Duration suspendDuration = watch.getDuration();
499 final long stopTimeMillis = watch.getStopTime();
500 final Instant stopInstant = watch.getStopInstant();
501
502 assertTrue(testStartMillis <= stopTimeMillis, () -> String.format("testStartMillis %s <= stopTimeMillis %s", testStartMillis, stopTimeMillis));
503 assertTrue(testStartInstant.isBefore(stopInstant), () -> String.format("testStartInstant %s < stopInstant %s", testStartInstant, stopInstant));
504 assertTrue(testSuspendMillis <= stopTimeMillis, () -> String.format("testSuspendMillis %s <= stopTimeMillis %s", testSuspendMillis, stopTimeMillis));
505 assertTrue(testSuspendMillis <= stopInstant.toEpochMilli(),
506 () -> String.format("testSuspendMillis %s <= stopInstant %s", testSuspendMillis, stopInstant));
507
508 sleepPlus1(sleepDuration);
509 watch.resume();
510 sleepPlus1(sleepDuration);
511 watch.stop();
512 final long totalTimeFromNanos = watch.getTime();
513 final Duration totalDuration = watch.getDuration();
514
515 assertTrue(suspendTimeFromNanos >= sleepMillis, () -> String.format("suspendTimeFromNanos %s >= sleepMillis %s", suspendTimeFromNanos, sleepMillis));
516 assertTrue(suspendDuration.compareTo(Duration.ofMillis(sleepMillis)) >= 0,
517 () -> String.format("suspendDuration %s >= sleepMillis %s", suspendDuration, sleepMillis));
518 assertTrue(suspendTimeFromNanos <= testSuspendTimeNanos,
519 () -> String.format("suspendTimeFromNanos %s <= testSuspendTimeNanos %s", suspendTimeFromNanos, testSuspendTimeNanos));
520 assertTrue(suspendDuration.compareTo(testSuspendDuration) <= 0,
521 () -> String.format("suspendDuration %s <= testSuspendDuration %s", suspendDuration, testSuspendDuration));
522
523 final long sleepMillisX2 = sleepMillis + sleepMillis;
524 assertTrue(totalTimeFromNanos >= sleepMillisX2, () -> String.format("totalTimeFromNanos %s >= sleepMillisX2 %s", totalTimeFromNanos, sleepMillisX2));
525 assertTrue(totalDuration.compareTo(Duration.ofMillis(sleepMillisX2)) >= 0,
526 () -> String.format("totalDuration >= sleepMillisX2", totalDuration, sleepMillisX2));
527
528
529 final long testTooLongMillis = sleepMillis * 100;
530 assertTrue(totalTimeFromNanos < testTooLongMillis,
531 () -> String.format("totalTimeFromNanos %s < testTooLongMillis %s", totalTimeFromNanos, testTooLongMillis));
532 assertTrue(totalDuration.compareTo(Duration.ofMillis(testTooLongMillis)) < 0,
533 () -> String.format("totalDuration %s < testTooLongMillis %s", totalDuration, testTooLongMillis));
534
535 }
536
537 @Test
538 void testToSplitString() throws InterruptedException {
539 final StopWatch watch = StopWatch.createStarted();
540 sleepPlus1(MIN_DURATION);
541 watch.split();
542 final String splitStr = watch.toSplitString();
543 assertEquals(SPLIT_CLOCK_STR_LEN, splitStr.length(), "Formatted split string not the correct length");
544 }
545
546 @Test
547 void testToSplitStringWithMessage() throws InterruptedException {
548 final StopWatch watch = new StopWatch(MESSAGE);
549 watch.start();
550 sleepPlus1(MIN_DURATION);
551 watch.split();
552 final String splitStr = watch.toSplitString();
553 assertEquals(SPLIT_CLOCK_STR_LEN + MESSAGE.length() + 1, splitStr.length(), "Formatted split string not the correct length");
554 }
555
556 @Test
557 void testToString() throws InterruptedException {
558
559 final StopWatch watch = StopWatch.createStarted();
560 sleepPlus1(MIN_DURATION);
561 watch.split();
562 final String splitStr = watch.toString();
563 assertEquals(SPLIT_CLOCK_STR_LEN, splitStr.length(), "Formatted split string not the correct length");
564 }
565
566 @Test
567 void testToStringWithMessage() throws InterruptedException {
568 assertTrue(new StopWatch(MESSAGE).toString().startsWith(MESSAGE), "message");
569
570 final StopWatch watch = new StopWatch(MESSAGE);
571 watch.start();
572 sleepPlus1(MIN_DURATION);
573 watch.split();
574 final String splitStr = watch.toString();
575 assertEquals(SPLIT_CLOCK_STR_LEN + MESSAGE.length() + 1, splitStr.length(), "Formatted split string not the correct length");
576 }
577
578 private int throwIOException() throws IOException {
579 throw new IOException("A");
580 }
581 }