1 /*
2  * Copyright (C) 2006 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static java.util.concurrent.TimeUnit.MILLISECONDS;
21 
22 import com.google.common.base.Stopwatch;
23 import com.google.common.collect.Range;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 import java.util.concurrent.TimeoutException;
29 import junit.framework.TestCase;
30 
31 /**
32  * Unit test for {@link SimpleTimeLimiter}.
33  *
34  * @author kevinb
35  * @author Jens Nyman
36  */
37 
38 public class SimpleTimeLimiterTest extends TestCase {
39 
40   private static final long DELAY_MS = 50;
41   private static final long ENOUGH_MS = 10000;
42   private static final long NOT_ENOUGH_MS = 5;
43 
44   private static final String GOOD_CALLABLE_RESULT = "good callable result";
45   private static final Callable<String> GOOD_CALLABLE =
46       new Callable<String>() {
47         @Override
48         public String call() throws InterruptedException {
49           MILLISECONDS.sleep(DELAY_MS);
50           return GOOD_CALLABLE_RESULT;
51         }
52       };
53   private static final Callable<String> BAD_CALLABLE =
54       new Callable<String>() {
55         @Override
56         public String call() throws InterruptedException, SampleException {
57           MILLISECONDS.sleep(DELAY_MS);
58           throw new SampleException();
59         }
60       };
61   private static final Runnable GOOD_RUNNABLE =
62       new Runnable() {
63         @Override
64         public void run() {
65           try {
66             MILLISECONDS.sleep(DELAY_MS);
67           } catch (InterruptedException e) {
68             throw new RuntimeException(e);
69           }
70         }
71       };
72   private static final Runnable BAD_RUNNABLE =
73       new Runnable() {
74         @Override
75         public void run() {
76           try {
77             MILLISECONDS.sleep(DELAY_MS);
78           } catch (InterruptedException e) {
79             throw new RuntimeException(e);
80           }
81           throw new SampleRuntimeException();
82         }
83       };
84 
85   private TimeLimiter service;
86 
87   private static final ExecutorService executor = Executors.newFixedThreadPool(1);
88 
89   @Override
setUp()90   protected void setUp() throws Exception {
91     super.setUp();
92     service = SimpleTimeLimiter.create(executor);
93   }
94 
testNewProxy_goodMethodWithEnoughTime()95   public void testNewProxy_goodMethodWithEnoughTime() throws Exception {
96     SampleImpl target = new SampleImpl(DELAY_MS);
97     Sample proxy = service.newProxy(target, Sample.class, ENOUGH_MS, MILLISECONDS);
98     Stopwatch stopwatch = Stopwatch.createStarted();
99 
100     String result = proxy.sleepThenReturnInput("x");
101 
102     assertThat(result).isEqualTo("x");
103     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS));
104     assertThat(target.finished).isTrue();
105   }
106 
testNewProxy_goodMethodWithNotEnoughTime()107   public void testNewProxy_goodMethodWithNotEnoughTime() throws Exception {
108     SampleImpl target = new SampleImpl(9999);
109     Sample proxy = service.newProxy(target, Sample.class, NOT_ENOUGH_MS, MILLISECONDS);
110     Stopwatch stopwatch = Stopwatch.createStarted();
111 
112     try {
113       proxy.sleepThenReturnInput("x");
114       fail("no exception thrown");
115     } catch (UncheckedTimeoutException expected) {
116     }
117 
118     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(NOT_ENOUGH_MS, DELAY_MS * 2));
119     // Is it still computing away anyway?
120     assertThat(target.finished).isFalse();
121     MILLISECONDS.sleep(ENOUGH_MS);
122     assertThat(target.finished).isFalse();
123   }
124 
testNewProxy_badMethodWithEnoughTime()125   public void testNewProxy_badMethodWithEnoughTime() throws Exception {
126     SampleImpl target = new SampleImpl(DELAY_MS);
127     Sample proxy = service.newProxy(target, Sample.class, ENOUGH_MS, MILLISECONDS);
128     Stopwatch stopwatch = Stopwatch.createStarted();
129 
130     try {
131       proxy.sleepThenThrowException();
132       fail("no exception thrown");
133     } catch (SampleException expected) {
134     }
135 
136     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS));
137   }
138 
testNewProxy_badMethodWithNotEnoughTime()139   public void testNewProxy_badMethodWithNotEnoughTime() throws Exception {
140     SampleImpl target = new SampleImpl(9999);
141     Sample proxy = service.newProxy(target, Sample.class, NOT_ENOUGH_MS, MILLISECONDS);
142     Stopwatch stopwatch = Stopwatch.createStarted();
143 
144     try {
145       proxy.sleepThenThrowException();
146       fail("no exception thrown");
147     } catch (UncheckedTimeoutException expected) {
148     }
149 
150     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(NOT_ENOUGH_MS, DELAY_MS * 2));
151   }
152 
testCallWithTimeout_goodCallableWithEnoughTime()153   public void testCallWithTimeout_goodCallableWithEnoughTime() throws Exception {
154     Stopwatch stopwatch = Stopwatch.createStarted();
155 
156     String result = service.callWithTimeout(GOOD_CALLABLE, ENOUGH_MS, MILLISECONDS);
157 
158     assertThat(result).isEqualTo(GOOD_CALLABLE_RESULT);
159     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS));
160   }
161 
testCallWithTimeout_goodCallableWithNotEnoughTime()162   public void testCallWithTimeout_goodCallableWithNotEnoughTime() throws Exception {
163     try {
164       service.callWithTimeout(GOOD_CALLABLE, NOT_ENOUGH_MS, MILLISECONDS);
165       fail("no exception thrown");
166     } catch (TimeoutException expected) {
167     }
168   }
169 
testCallWithTimeout_badCallableWithEnoughTime()170   public void testCallWithTimeout_badCallableWithEnoughTime() throws Exception {
171     try {
172       service.callWithTimeout(BAD_CALLABLE, ENOUGH_MS, MILLISECONDS);
173       fail("no exception thrown");
174     } catch (ExecutionException expected) {
175       assertThat(expected.getCause()).isInstanceOf(SampleException.class);
176     }
177   }
178 
testCallUninterruptiblyWithTimeout_goodCallableWithEnoughTime()179   public void testCallUninterruptiblyWithTimeout_goodCallableWithEnoughTime() throws Exception {
180     Stopwatch stopwatch = Stopwatch.createStarted();
181 
182     String result = service.callUninterruptiblyWithTimeout(GOOD_CALLABLE, ENOUGH_MS, MILLISECONDS);
183 
184     assertThat(result).isEqualTo(GOOD_CALLABLE_RESULT);
185     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS));
186   }
187 
testCallUninterruptiblyWithTimeout_goodCallableWithNotEnoughTime()188   public void testCallUninterruptiblyWithTimeout_goodCallableWithNotEnoughTime() throws Exception {
189     try {
190       service.callUninterruptiblyWithTimeout(GOOD_CALLABLE, NOT_ENOUGH_MS, MILLISECONDS);
191       fail("no exception thrown");
192     } catch (TimeoutException expected) {
193     }
194   }
195 
testCallUninterruptiblyWithTimeout_badCallableWithEnoughTime()196   public void testCallUninterruptiblyWithTimeout_badCallableWithEnoughTime() throws Exception {
197     try {
198       service.callUninterruptiblyWithTimeout(BAD_CALLABLE, ENOUGH_MS, MILLISECONDS);
199       fail("no exception thrown");
200     } catch (ExecutionException expected) {
201       assertThat(expected.getCause()).isInstanceOf(SampleException.class);
202     }
203   }
204 
testRunWithTimeout_goodRunnableWithEnoughTime()205   public void testRunWithTimeout_goodRunnableWithEnoughTime() throws Exception {
206     Stopwatch stopwatch = Stopwatch.createStarted();
207 
208     service.runWithTimeout(GOOD_RUNNABLE, ENOUGH_MS, MILLISECONDS);
209 
210     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS));
211   }
212 
testRunWithTimeout_goodRunnableWithNotEnoughTime()213   public void testRunWithTimeout_goodRunnableWithNotEnoughTime() throws Exception {
214     try {
215       service.runWithTimeout(GOOD_RUNNABLE, NOT_ENOUGH_MS, MILLISECONDS);
216       fail("no exception thrown");
217     } catch (TimeoutException expected) {
218     }
219   }
220 
testRunWithTimeout_badRunnableWithEnoughTime()221   public void testRunWithTimeout_badRunnableWithEnoughTime() throws Exception {
222     try {
223       service.runWithTimeout(BAD_RUNNABLE, ENOUGH_MS, MILLISECONDS);
224       fail("no exception thrown");
225     } catch (UncheckedExecutionException expected) {
226       assertThat(expected.getCause()).isInstanceOf(SampleRuntimeException.class);
227     }
228   }
229 
testRunUninterruptiblyWithTimeout_goodRunnableWithEnoughTime()230   public void testRunUninterruptiblyWithTimeout_goodRunnableWithEnoughTime() throws Exception {
231     Stopwatch stopwatch = Stopwatch.createStarted();
232 
233     service.runUninterruptiblyWithTimeout(GOOD_RUNNABLE, ENOUGH_MS, MILLISECONDS);
234 
235     assertThat(stopwatch.elapsed(MILLISECONDS)).isIn(Range.closed(DELAY_MS, ENOUGH_MS));
236   }
237 
testRunUninterruptiblyWithTimeout_goodRunnableWithNotEnoughTime()238   public void testRunUninterruptiblyWithTimeout_goodRunnableWithNotEnoughTime() throws Exception {
239     try {
240       service.runUninterruptiblyWithTimeout(GOOD_RUNNABLE, NOT_ENOUGH_MS, MILLISECONDS);
241       fail("no exception thrown");
242     } catch (TimeoutException expected) {
243     }
244   }
245 
testRunUninterruptiblyWithTimeout_badRunnableWithEnoughTime()246   public void testRunUninterruptiblyWithTimeout_badRunnableWithEnoughTime() throws Exception {
247     try {
248       service.runUninterruptiblyWithTimeout(BAD_RUNNABLE, ENOUGH_MS, MILLISECONDS);
249       fail("no exception thrown");
250     } catch (UncheckedExecutionException expected) {
251       assertThat(expected.getCause()).isInstanceOf(SampleRuntimeException.class);
252     }
253   }
254 
255   private interface Sample {
sleepThenReturnInput(String input)256     String sleepThenReturnInput(String input);
257 
sleepThenThrowException()258     void sleepThenThrowException() throws SampleException;
259   }
260 
261   @SuppressWarnings("serial")
262   private static class SampleException extends Exception {}
263 
264   @SuppressWarnings("serial")
265   private static class SampleRuntimeException extends RuntimeException {}
266 
267   private static class SampleImpl implements Sample {
268     final long delayMillis;
269     boolean finished;
270 
SampleImpl(long delayMillis)271     SampleImpl(long delayMillis) {
272       this.delayMillis = delayMillis;
273     }
274 
275     @Override
sleepThenReturnInput(String input)276     public String sleepThenReturnInput(String input) {
277       try {
278         MILLISECONDS.sleep(delayMillis);
279         finished = true;
280         return input;
281       } catch (InterruptedException e) {
282         return null;
283       }
284     }
285 
286     @Override
sleepThenThrowException()287     public void sleepThenThrowException() throws SampleException {
288       try {
289         MILLISECONDS.sleep(delayMillis);
290       } catch (InterruptedException e) {
291       }
292       throw new SampleException();
293     }
294   }
295 }
296