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
17package com.google.common.util.concurrent;
18
19import junit.framework.TestCase;
20
21import java.util.concurrent.Callable;
22import java.util.concurrent.ExecutorService;
23import java.util.concurrent.Executors;
24import java.util.concurrent.TimeUnit;
25
26/**
27 * Unit test for {@link SimpleTimeLimiter}.
28 *
29 * @author kevinb
30 */
31public class SimpleTimeLimiterTest extends TestCase {
32
33  private static final int DELAY_MS = 50;
34  private static final int ENOUGH_MS = 500;
35  private static final int NOT_ENOUGH_MS = 5;
36
37  private TimeLimiter service;
38
39  private static final ExecutorService executor
40      = Executors.newFixedThreadPool(1);
41
42  private static String someGoodStaticMethod() throws InterruptedException {
43    TimeUnit.MILLISECONDS.sleep(DELAY_MS);
44    return "yes";
45  }
46
47  private static String someBadStaticMethod() throws InterruptedException,
48      SampleException {
49    TimeUnit.MILLISECONDS.sleep(DELAY_MS);
50    throw new SampleException();
51  }
52
53  @Override protected void setUp() throws Exception {
54    super.setUp();
55    service = new SimpleTimeLimiter(executor);
56  }
57
58  public void testGoodCallableWithEnoughTime() throws Exception {
59    long start = System.nanoTime();
60    String result = service.callWithTimeout(
61        new Callable<String>() {
62          @Override
63          public String call() throws InterruptedException {
64            return someGoodStaticMethod();
65          }
66        }, ENOUGH_MS, TimeUnit.MILLISECONDS, true);
67    assertEquals("yes", result);
68    assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
69  }
70
71  public void testGoodCallableWithNotEnoughTime() throws Exception {
72    long start = System.nanoTime();
73    try {
74      service.callWithTimeout(
75          new Callable<String>() {
76            @Override
77            public String call() throws InterruptedException {
78              return someGoodStaticMethod();
79            }
80          }, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS, true);
81      fail("no exception thrown");
82    } catch (UncheckedTimeoutException expected) {
83    }
84    assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
85  }
86
87  public void testBadCallableWithEnoughTime() throws Exception {
88    long start = System.nanoTime();
89    try {
90      service.callWithTimeout(
91          new Callable<String>() {
92            @Override
93            public String call() throws SampleException, InterruptedException {
94              return someBadStaticMethod();
95            }
96          }, ENOUGH_MS, TimeUnit.MILLISECONDS, true);
97      fail("no exception thrown");
98    } catch (SampleException expected) {
99    }
100    assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
101  }
102
103  public void testBadCallableWithNotEnoughTime() throws Exception {
104    long start = System.nanoTime();
105    try {
106      service.callWithTimeout(
107          new Callable<String>() {
108            @Override
109            public String call() throws SampleException, InterruptedException {
110              return someBadStaticMethod();
111            }
112          }, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS, true);
113      fail("no exception thrown");
114    } catch (UncheckedTimeoutException expected) {
115    }
116    assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
117  }
118
119  public void testGoodMethodWithEnoughTime() throws Exception {
120    SampleImpl target = new SampleImpl();
121    Sample proxy = service.newProxy(
122        target, Sample.class, ENOUGH_MS, TimeUnit.MILLISECONDS);
123    long start = System.nanoTime();
124    assertEquals("x", proxy.sleepThenReturnInput("x"));
125    assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
126    assertTrue(target.finished);
127  }
128
129  public void testGoodMethodWithNotEnoughTime() throws Exception {
130    SampleImpl target = new SampleImpl();
131    Sample proxy = service.newProxy(
132        target, Sample.class, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS);
133    long start = System.nanoTime();
134    try {
135      proxy.sleepThenReturnInput("x");
136      fail("no exception thrown");
137    } catch (UncheckedTimeoutException expected) {
138    }
139    assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
140
141    // Is it still computing away anyway?
142    assertFalse(target.finished);
143    TimeUnit.MILLISECONDS.sleep(ENOUGH_MS);
144    assertFalse(target.finished);
145  }
146
147  public void testBadMethodWithEnoughTime() throws Exception {
148    SampleImpl target = new SampleImpl();
149    Sample proxy = service.newProxy(
150        target, Sample.class, ENOUGH_MS, TimeUnit.MILLISECONDS);
151    long start = System.nanoTime();
152    try {
153      proxy.sleepThenThrowException();
154      fail("no exception thrown");
155    } catch (SampleException expected) {
156    }
157    assertTheCallTookBetween(start, DELAY_MS, ENOUGH_MS);
158  }
159
160  public void testBadMethodWithNotEnoughTime() throws Exception {
161    SampleImpl target = new SampleImpl();
162    Sample proxy = service.newProxy(
163        target, Sample.class, NOT_ENOUGH_MS, TimeUnit.MILLISECONDS);
164    long start = System.nanoTime();
165    try {
166      proxy.sleepThenThrowException();
167      fail("no exception thrown");
168    } catch (UncheckedTimeoutException expected) {
169    }
170    assertTheCallTookBetween(start, NOT_ENOUGH_MS, DELAY_MS);
171  }
172
173  private static void assertTheCallTookBetween(
174      long startNanos, int atLeastMillis, int atMostMillis) {
175    long nanos = System.nanoTime() - startNanos;
176    assertTrue(nanos >= atLeastMillis * 1000000);
177    assertTrue(nanos <= atMostMillis * 1000000);
178  }
179
180  public interface Sample {
181    String sleepThenReturnInput(String input);
182    void sleepThenThrowException() throws SampleException;
183  }
184
185  @SuppressWarnings("serial")
186  public static class SampleException extends Exception { }
187
188  public static class SampleImpl implements Sample {
189    boolean finished;
190
191    @Override
192    public String sleepThenReturnInput(String input) {
193      try {
194        TimeUnit.MILLISECONDS.sleep(DELAY_MS);
195        finished = true;
196        return input;
197      } catch (InterruptedException e) {
198        return null;
199      }
200    }
201    @Override
202    public void sleepThenThrowException() throws SampleException {
203      try {
204        TimeUnit.MILLISECONDS.sleep(DELAY_MS);
205      } catch (InterruptedException e) {
206      }
207      throw new SampleException();
208    }
209  }
210}
211