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    *      http://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.hamcrest.MatcherAssert.assertThat;
20  import static org.hamcrest.Matchers.allOf;
21  import static org.hamcrest.Matchers.greaterThanOrEqualTo;
22  import static org.hamcrest.Matchers.lessThan;
23  import static org.hamcrest.Matchers.lessThanOrEqualTo;
24  import static org.hamcrest.Matchers.not;
25  import static org.hamcrest.Matchers.startsWith;
26  import static org.junit.jupiter.api.Assertions.assertEquals;
27  import static org.junit.jupiter.api.Assertions.assertFalse;
28  import static org.junit.jupiter.api.Assertions.assertNotEquals;
29  import static org.junit.jupiter.api.Assertions.assertNull;
30  import static org.junit.jupiter.api.Assertions.assertThrows;
31  import static org.junit.jupiter.api.Assertions.assertTrue;
32  
33  import java.time.Duration;
34  import java.time.Instant;
35  import java.util.concurrent.TimeUnit;
36  
37  import org.apache.commons.lang3.AbstractLangTest;
38  import org.apache.commons.lang3.ThreadUtils;
39  import org.apache.commons.lang3.reflect.FieldUtils;
40  import org.junit.jupiter.api.Test;
41  
42  /**
43   * Tests {@link StopWatch}.
44   */
45  public class StopWatchTest extends AbstractLangTest {
46  
47      private static final Duration MILLIS_200 = Duration.ofMillis(200);
48      private static final Duration MILLIS_550 = Duration.ofMillis(550);
49      private static final String MESSAGE = "Baking cookies";
50      private static final Duration MIN_SLEEP = Duration.ofMillis(20);
51      private static final String ZERO_HOURS_PREFIX = "00:";
52      private static final String ZERO_TIME_ELAPSED = "00:00:00.000";
53  
54      /**
55       * <p>
56       * Creates a suspended StopWatch object which appears to have elapsed for the requested amount of time in
57       * nanoseconds.
58       * <p>
59       * <p>
60       *
61       * <pre>
62       * // Create a mock StopWatch with a time of 2:59:01.999
63       * final long nanos = TimeUnit.HOURS.toNanos(2)
64       *         + TimeUnit.MINUTES.toNanos(59)
65       *         + TimeUnit.SECONDS.toNanos(1)
66       *         + TimeUnit.MILLISECONDS.toNanos(999);
67       * final StopWatch watch = createMockStopWatch(nanos);
68       * </pre>
69       *
70       * @param nanos Time in nanoseconds to have elapsed on the stop watch
71       * @return StopWatch in a suspended state with the elapsed time
72       */
73      private StopWatch createMockStopWatch(final long nanos) {
74          final StopWatch watch = StopWatch.createStarted();
75          watch.suspend();
76          return set(watch, nanos);
77      }
78  
79      private StopWatch set(final StopWatch watch, final long nanos) {
80          try {
81              final long currentNanos = System.nanoTime();
82              FieldUtils.writeField(watch, "startTimeNanos", currentNanos - nanos, true);
83              FieldUtils.writeField(watch, "stopTimeNanos", currentNanos, true);
84          } catch (final IllegalAccessException e) {
85              return null;
86          }
87          return watch;
88      }
89  
90      private void sleep(final Duration duration) throws InterruptedException {
91          ThreadUtils.sleep(duration);
92      }
93  
94      /**
95       * Tests bad states.
96       */
97      @Test
98      public void testBadStates() {
99          final StopWatch watch = new StopWatch();
100         assertThrows(IllegalStateException.class, watch::stop,
101             "Calling stop on an unstarted StopWatch should throw an exception. ");
102 
103         assertThrows(IllegalStateException.class, watch::suspend,
104             "Calling suspend on an unstarted StopWatch should throw an exception. ");
105 
106         assertThrows(IllegalStateException.class, watch::split,
107             "Calling split on a non-running StopWatch should throw an exception. ");
108 
109         assertThrows(IllegalStateException.class, watch::unsplit,
110             "Calling unsplit on an unsplit StopWatch should throw an exception. ");
111 
112         assertThrows(IllegalStateException.class, watch::resume,
113             "Calling resume on an unsuspended StopWatch should throw an exception. ");
114 
115         watch.start();
116 
117         assertThrows(IllegalStateException.class, watch::start,
118             "Calling start on a started StopWatch should throw an exception. ");
119 
120         assertThrows(IllegalStateException.class, watch::unsplit,
121             "Calling unsplit on an unsplit StopWatch should throw an exception. ");
122 
123         assertThrows(IllegalStateException.class, watch::getSplitTime,
124                 "Calling getSplitTime on an unsplit StopWatch should throw an exception. ");
125 
126         assertThrows(IllegalStateException.class, watch::getSplitDuration,
127                 "Calling getSplitTime on an unsplit StopWatch should throw an exception. ");
128 
129         assertThrows(IllegalStateException.class, watch::resume,
130             "Calling resume on an unsuspended StopWatch should throw an exception. ");
131 
132         watch.stop();
133 
134         assertThrows(IllegalStateException.class, watch::start,
135             "Calling start on a stopped StopWatch should throw an exception as it needs to be reset. ");
136     }
137 
138     @Test
139     public void testBooleanStates() {
140         final StopWatch watch = new StopWatch();
141         assertFalse(watch.isStarted());
142         assertFalse(watch.isSuspended());
143         assertTrue(watch.isStopped());
144 
145         watch.start();
146         assertTrue(watch.isStarted());
147         assertFalse(watch.isSuspended());
148         assertFalse(watch.isStopped());
149 
150         watch.suspend();
151         assertTrue(watch.isStarted());
152         assertTrue(watch.isSuspended());
153         assertFalse(watch.isStopped());
154 
155         watch.stop();
156         assertFalse(watch.isStarted());
157         assertFalse(watch.isSuspended());
158         assertTrue(watch.isStopped());
159     }
160 
161     @Test
162     public void testFormatSplitTime() {
163         final StopWatch watch = StopWatch.createStarted();
164         ThreadUtils.sleepQuietly(MIN_SLEEP);
165         watch.split();
166         final String formatSplitTime = watch.formatSplitTime();
167         assertNotEquals(ZERO_TIME_ELAPSED, formatSplitTime);
168         assertThat("formatSplitTime", formatSplitTime, startsWith(ZERO_HOURS_PREFIX));
169     }
170 
171     @Test
172     public void testFormatSplitTimeWithMessage() {
173         final StopWatch watch = new StopWatch(MESSAGE);
174         watch.start();
175         ThreadUtils.sleepQuietly(MIN_SLEEP);
176         watch.split();
177         final String formatSplitTime = watch.formatSplitTime();
178         assertThat("formatSplitTime", formatSplitTime, not(startsWith(MESSAGE)));
179         assertThat("formatSplitTime", formatSplitTime, startsWith(ZERO_HOURS_PREFIX));
180     }
181 
182     @Test
183     public void testFormatTime() {
184         final StopWatch watch = StopWatch.create();
185         final String formatTime = watch.formatTime();
186         assertEquals(ZERO_TIME_ELAPSED, formatTime);
187         assertThat("formatTime", formatTime, startsWith(ZERO_HOURS_PREFIX));
188     }
189 
190     @Test
191     public void testFormatTimeWithMessage() {
192         final StopWatch watch = new StopWatch(MESSAGE);
193         final String formatTime = watch.formatTime();
194         assertThat("formatTime", formatTime, not(startsWith(MESSAGE)));
195     }
196 
197     @Test
198     public void testGetDuration() throws InterruptedException {
199         final StopWatch watch = new StopWatch();
200         assertEquals(Duration.ZERO, watch.getDuration());
201         assertEquals(ZERO_TIME_ELAPSED, watch.toString());
202         watch.start();
203         sleep(MILLIS_550);
204         assertThat("watch.getDuration()", watch.getDuration().toMillis(), lessThan(2000L));
205     }
206 
207     @Test
208     public void testGetSplitDuration() {
209         // Create a mock StopWatch with a time of 2:59:01.999
210         // @formatter:off
211         final StopWatch watch = StopWatch.createStarted();
212         watch.split();
213         set(watch, 123456);
214         // @formatter:on
215         assertEquals(Duration.ofNanos(123456), watch.getSplitDuration());
216     }
217 
218     @Test
219     public void testGetStartInstant() {
220         final long beforeStopWatchMillis = System.currentTimeMillis();
221         final StopWatch watch = new StopWatch();
222         assertThrows(IllegalStateException.class, watch::getStartInstant, "Calling getStartInstant on an unstarted StopWatch should throw an exception");
223         watch.start();
224 
225         watch.getStartInstant();
226         assertThat("getStartInstant", watch.getStartInstant(), greaterThanOrEqualTo(Instant.ofEpochMilli(beforeStopWatchMillis)));
227 
228         watch.reset();
229         assertThrows(IllegalStateException.class, watch::getStartInstant,
230                 "Calling getStartInstant on a reset, but unstarted StopWatch should throw an exception");
231     }
232 
233     @Test
234     public void testGetStartTime() {
235         final long beforeStopWatchMillis = System.currentTimeMillis();
236         final StopWatch watch = new StopWatch();
237         assertThrows(IllegalStateException.class, watch::getStartTime, "Calling getStartTime on an unstarted StopWatch should throw an exception");
238         watch.start();
239 
240         watch.getStartTime();
241         assertThat("getStartTime", watch.getStartTime(), greaterThanOrEqualTo(beforeStopWatchMillis));
242 
243         watch.reset();
244         assertThrows(IllegalStateException.class, watch::getStartTime, "Calling getStartTime on a reset, but unstarted StopWatch should throw an exception");
245     }
246 
247     @Test
248     public void testGetTime() throws InterruptedException {
249         final StopWatch watch = new StopWatch();
250         assertEquals(0, watch.getTime());
251         assertEquals(ZERO_TIME_ELAPSED, watch.toString());
252         watch.start();
253         sleep(MILLIS_550);
254         assertThat("watch.getTime()", watch.getTime(), lessThan(2000L));
255     }
256 
257     @Test
258     public void testGetWithTimeUnit() {
259         // Create a mock StopWatch with a time of 2:59:01.999
260         // @formatter:off
261         final StopWatch watch = createMockStopWatch(
262             TimeUnit.HOURS.toNanos(2)
263                     + TimeUnit.MINUTES.toNanos(59)
264                     + TimeUnit.SECONDS.toNanos(1)
265                     + TimeUnit.MILLISECONDS.toNanos(999));
266         // @formatter:on
267         assertEquals(2L, watch.getTime(TimeUnit.HOURS));
268         assertEquals(179L, watch.getTime(TimeUnit.MINUTES));
269         assertEquals(10741L, watch.getTime(TimeUnit.SECONDS));
270         assertEquals(10741999L, watch.getTime(TimeUnit.MILLISECONDS));
271     }
272 
273     @Test
274     public void testLang315() throws InterruptedException {
275         final StopWatch watch = StopWatch.createStarted();
276         sleep(MILLIS_200);
277         watch.suspend();
278         final long suspendTime = watch.getTime();
279         final Duration suspendDuration = watch.getDuration();
280         sleep(MILLIS_200);
281         watch.stop();
282         final long totalTime = watch.getTime();
283         final Duration totalDuration = watch.getDuration();
284         assertEquals(suspendTime, totalTime);
285         assertEquals(suspendDuration, totalDuration);
286     }
287 
288     @Test
289     public void testMessage() {
290         assertNull(StopWatch.create().getMessage());
291         final StopWatch stopWatch = new StopWatch(MESSAGE);
292         assertEquals(MESSAGE, stopWatch.getMessage());
293         assertThat("stopWatch.toString", stopWatch.toString(), startsWith(MESSAGE));
294         stopWatch.start();
295         stopWatch.split();
296         assertThat("stopWatch.toSplitString", stopWatch.toSplitString(), startsWith(MESSAGE));
297     }
298 
299     @Test
300     public void testSimple() throws InterruptedException {
301         final StopWatch watch = StopWatch.createStarted();
302         sleep(MILLIS_550);
303         watch.stop();
304         final long time = watch.getTime();
305         final Duration duration = watch.getDuration();
306         assertEquals(time, watch.getTime());
307         assertEquals(duration, watch.getDuration());
308         assertThat("time", time, allOf(greaterThanOrEqualTo(500L), lessThan(2000L)));
309         assertThat("duration", duration.toMillis(), allOf(greaterThanOrEqualTo(500L), lessThan(2000L)));
310         watch.reset();
311         assertEquals(0, watch.getTime());
312         assertEquals(Duration.ZERO, watch.getDuration());
313     }
314 
315     @Test
316     public void testSplit() throws InterruptedException {
317         final StopWatch watch = StopWatch.createStarted();
318         sleep(MILLIS_550);
319         // slept ~550 millis
320         watch.split();
321         final long splitTime = watch.getSplitTime();
322         final Duration splitDuration = watch.getSplitDuration();
323         assertEquals(splitTime, watch.getSplitDuration().toMillis());
324         assertEquals(12, watch.toSplitString().length(), "Formatted split string not the correct length");
325         sleep(MILLIS_550);
326         // slept ~1100 millis
327         watch.unsplit();
328         sleep(MILLIS_550);
329         // slept ~1650 millis
330         watch.stop();
331         final long totalTime = watch.getTime();
332         final Duration totalDuration = watch.getDuration();
333         assertThat("splitTime", splitTime, allOf(greaterThanOrEqualTo(500L), lessThan(1000L)));
334         assertThat("splitDuration", splitDuration.toMillis(), allOf(greaterThanOrEqualTo(500L), lessThan(1000L)));
335         assertThat("totalTime", totalTime, allOf(greaterThanOrEqualTo(1500L), lessThan(2100L)));
336         assertThat("totalDuration", totalDuration.toMillis(), allOf(greaterThanOrEqualTo(1500L), lessThan(2100L)));
337     }
338 
339     @Test
340     public void testStatic() {
341         final StopWatch watch = StopWatch.createStarted();
342         assertTrue(watch.isStarted());
343     }
344 
345     @Test
346     public void testStopInstantSimple() throws InterruptedException {
347         final StopWatch watch = StopWatch.createStarted();
348         final long testStartMillis = System.currentTimeMillis();
349         sleep(MILLIS_550);
350         watch.stop();
351         final long testEndMillis = System.currentTimeMillis();
352         final Instant stopTime = watch.getStopInstant();
353         assertEquals(stopTime, watch.getStopInstant());
354         assertThat("stopTime", stopTime,
355                 allOf(greaterThanOrEqualTo(Instant.ofEpochMilli(testStartMillis)), lessThanOrEqualTo(Instant.ofEpochMilli(testEndMillis))));
356     }
357 
358     @Test
359     public void testStopTimeSimple() throws InterruptedException {
360         final StopWatch watch = StopWatch.createStarted();
361         final long testStartMillis = System.currentTimeMillis();
362         sleep(MILLIS_550);
363         watch.stop();
364         final long testEndMillis = System.currentTimeMillis();
365         final long stopTime = watch.getStopTime();
366         assertEquals(stopTime, watch.getStopTime());
367         assertThat("stopTime", stopTime, allOf(greaterThanOrEqualTo(testStartMillis), lessThanOrEqualTo(testEndMillis)));
368     }
369 
370     @Test
371     public void testSuspend() throws InterruptedException {
372         // Watch out comparing measurements from System.currentTimeMillis() vs. System.nanoTime()
373         final StopWatch watch = StopWatch.createStarted();
374         final long testStartMillis = System.currentTimeMillis();
375         final long testStartNanos = System.nanoTime();
376         final Instant testStartInstant = Instant.ofEpochMilli(testStartMillis);
377         sleep(MILLIS_550);
378         watch.suspend();
379         final long testSuspendMillis = System.currentTimeMillis();
380         final long testSuspendNanos = System.nanoTime();
381         final long testSuspendTimeNanos = testSuspendNanos - testStartNanos;
382         final Duration testSuspendDuration = Duration.ofNanos(testSuspendTimeNanos);
383         final long suspendTimeFromNanos = watch.getTime();
384         final Duration suspendDuration = watch.getDuration();
385         final long stopTimeMillis = watch.getStopTime();
386         final Instant stopInstant = watch.getStopInstant();
387 
388         assertThat("testStartMillis <= stopTimeMillis", testStartMillis, lessThanOrEqualTo(stopTimeMillis));
389         assertThat("testStartInstant <= stopInstant", testStartInstant, lessThanOrEqualTo(stopInstant));
390         assertThat("testSuspendMillis <= stopTimeMillis", testSuspendMillis, lessThanOrEqualTo(stopTimeMillis));
391         assertThat("testSuspendMillis <= stopInstant", testSuspendMillis, lessThanOrEqualTo(stopInstant.toEpochMilli()));
392 
393         sleep(MILLIS_550);
394         watch.resume();
395         sleep(MILLIS_550);
396         watch.stop();
397         final long totalTimeFromNanos = watch.getTime();
398         final Duration totalDuration = watch.getDuration();
399 
400         assertThat("suspendTimeFromNanos", suspendTimeFromNanos, greaterThanOrEqualTo(500L));
401         assertThat("suspendDuration", suspendDuration, greaterThanOrEqualTo(Duration.ofMillis(500L)));
402         assertThat("suspendTimeFromNanos <= testSuspendTimeNanos", suspendTimeFromNanos, lessThanOrEqualTo(testSuspendTimeNanos));
403         assertThat("suspendDuration <= testSuspendDuration", suspendDuration, lessThanOrEqualTo(testSuspendDuration));
404         assertThat("totalTimeFromNanos", totalTimeFromNanos, greaterThanOrEqualTo(1000L));
405         assertThat("totalDuration", totalDuration, greaterThanOrEqualTo(Duration.ofMillis(1000L)));
406         // Be lenient for slow running builds
407         assertThat("totalTimeFromNanos", totalTimeFromNanos, lessThan(2500L));
408         assertThat("totalDuration", totalDuration, lessThan(Duration.ofMillis(2500L)));
409     }
410 
411     @Test
412     public void testToSplitString() throws InterruptedException {
413         final StopWatch watch = StopWatch.createStarted();
414         sleep(MILLIS_550);
415         watch.split();
416         final String splitStr = watch.toSplitString();
417         assertEquals(12, splitStr.length(), "Formatted split string not the correct length");
418     }
419 
420     @Test
421     public void testToSplitStringWithMessage() throws InterruptedException {
422         final StopWatch watch = new StopWatch(MESSAGE);
423         watch.start();
424         sleep(MILLIS_550);
425         watch.split();
426         final String splitStr = watch.toSplitString();
427         assertEquals(12 + MESSAGE.length() + 1, splitStr.length(), "Formatted split string not the correct length");
428     }
429 
430     @Test
431     public void testToString() throws InterruptedException {
432         //
433         final StopWatch watch = StopWatch.createStarted();
434         sleep(MILLIS_550);
435         watch.split();
436         final String splitStr = watch.toString();
437         assertEquals(12, splitStr.length(), "Formatted split string not the correct length");
438     }
439 
440     @Test
441     public void testToStringWithMessage() throws InterruptedException {
442         assertThat("message", new StopWatch(MESSAGE).toString(), startsWith(MESSAGE));
443         //
444         final StopWatch watch = new StopWatch(MESSAGE);
445         watch.start();
446         sleep(MILLIS_550);
447         watch.split();
448         final String splitStr = watch.toString();
449         assertEquals(12 + MESSAGE.length() + 1, splitStr.length(), "Formatted split string not the correct length");
450     }
451 }