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