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 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       * 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 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 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         // test state
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         // test state
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         // Create a mock StopWatch with a time of 2:59:01.999
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         // Create a mock StopWatch with a time of 2:59:01.999
285         // @formatter:off
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         // @formatter:on
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         // test state
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         // test state
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         // starting splits
412         watch.split(firstLabel);
413         watch.split(secondLabel);
414         watch.split(thirdLabel);
415         watch.stop();
416         // getting splits
417         final List<StopWatch.Split> splits = watch.getSplits();
418         // check size
419         assertEquals(3, splits.size());
420         // check labels
421         assertEquals(firstLabel, splits.get(0).getLabel());
422         assertEquals(secondLabel, splits.get(1).getLabel());
423         assertEquals(thirdLabel, splits.get(2).getLabel());
424         // check time in nanos
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         // We can only unsplit once
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         // Only less than, not equal
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         // Only less than, not equal
478         assertTrue(testStartMillis < testEndMillis);
479     }
480 
481     @Test
482     void testSuspend() throws InterruptedException {
483         // Watch out comparing measurements from System.currentTimeMillis() vs. System.nanoTime()
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         // See sleepPlus1
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         // Be lenient for slow running builds
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 }