View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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   * Tests {@link StopWatch}.
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       * <p>
54       * Creates a suspended StopWatch object which appears to have elapsed for the requested amount of time in nanoseconds.
55       * <p>
56       *
57       * <pre>
58       * // Create a mock StopWatch with a time of 2:59:01.999
59       * final long nanos = TimeUnit.HOURS.toNanos(2) + TimeUnit.MINUTES.toNanos(59) + TimeUnit.SECONDS.toNanos(1) + TimeUnit.MILLISECONDS.toNanos(999);
60       * final StopWatch watch = createMockStopWatch(nanos);
61       * </pre>
62       *
63       * @param nanos Time in nanoseconds to have elapsed on the stop watch
64       * @return StopWatch in a suspended state with the elapsed time
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       * Sleeps the requested duration plus one millisecond. On Java 8, sleeping for 2 or 20 millis can sleep for a tiny bit less.
88       *
89       * @param duration How long to sleep.
90       * @throws InterruptedException Thrown if any thread has interrupted the current thread.
91       */
92      private void sleepPlus1(final Duration duration) throws InterruptedException {
93          ThreadUtils.sleep(duration.plusMillis(1));
94      }
95  
96      /**
97       * Tests bad states.
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         // Backdate the start so the split spans more than Integer.MAX_VALUE milliseconds (about 24.86 days).
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         // test state
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         // test state
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         // Create a mock StopWatch with a time of 2:59:01.999
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         // Create a mock StopWatch with a time of 2:59:01.999
302         // @formatter:off
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         // @formatter:on
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         // test state
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         // test state
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         // starting splits
429         watch.split(firstLabel);
430         watch.split(secondLabel);
431         watch.split(thirdLabel);
432         watch.stop();
433         // getting splits
434         final List<StopWatch.Split> splits = watch.getSplits();
435         // check size
436         assertEquals(3, splits.size());
437         // check labels
438         assertEquals(firstLabel, splits.get(0).getLabel());
439         assertEquals(secondLabel, splits.get(1).getLabel());
440         assertEquals(thirdLabel, splits.get(2).getLabel());
441         // check time in nanos
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         // We can only unsplit once
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         // Only less than, not equal
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         // Only less than, not equal
489         assertTrue(testStartMillis < testEndMillis);
490     }
491 
492     @Test
493     void testSuspend() throws InterruptedException {
494         // Watch out comparing measurements from System.currentTimeMillis() vs. System.nanoTime()
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         // See sleepPlus1
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         // Be lenient for slow running builds
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 }