• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 The gRPC 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 io.grpc;
18 
19 import static com.google.common.truth.Truth.assertAbout;
20 import static com.google.common.truth.Truth.assertThat;
21 import static io.grpc.testing.DeadlineSubject.deadline;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertSame;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.mock;
28 import static org.mockito.Mockito.verify;
29 
30 import com.google.common.testing.EqualsTester;
31 import com.google.common.truth.Truth;
32 import java.util.Arrays;
33 import java.util.concurrent.Future;
34 import java.util.concurrent.ScheduledExecutorService;
35 import java.util.concurrent.TimeUnit;
36 import java.util.concurrent.atomic.AtomicBoolean;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.Parameterized;
40 import org.junit.runners.Parameterized.Parameters;
41 import org.mockito.ArgumentCaptor;
42 
43 /**
44  * Tests for {@link Context}.
45  */
46 @RunWith(Parameterized.class)
47 public class DeadlineTest {
48   /** Ticker epochs to vary testing. */
49   @Parameters
data()50   public static Iterable<Object[]> data() {
51     return Arrays.asList(new Object[][] {
52       // MAX_VALUE / 2 is important because the signs are generally the same for past and future
53       // deadlines.
54       {Long.MAX_VALUE / 2}, {0}, {Long.MAX_VALUE}, {Long.MIN_VALUE}
55     });
56   }
57 
58   private FakeTicker ticker = new FakeTicker();
59 
DeadlineTest(long epoch)60   public DeadlineTest(long epoch) {
61     ticker.reset(epoch);
62   }
63 
64   @Test
defaultTickerIsSystemTicker()65   public void defaultTickerIsSystemTicker() {
66     Deadline d = Deadline.after(0, TimeUnit.SECONDS);
67     ticker.reset(System.nanoTime());
68     Deadline reference = Deadline.after(0, TimeUnit.SECONDS, ticker);
69     // Allow inaccuracy to account for system time advancing during test.
70     assertAbout(deadline()).that(d).isWithin(1, TimeUnit.SECONDS).of(reference);
71   }
72 
73   @Test
minimum()74   public void minimum() {
75     Deadline d1 = Deadline.after(1, TimeUnit.MINUTES, ticker);
76     Deadline d2 = Deadline.after(2, TimeUnit.MINUTES, ticker);
77     Deadline d3 = Deadline.after(3, TimeUnit.MINUTES, ticker);
78 
79     assertThat(d1.minimum(d2)).isSameInstanceAs(d1);
80     assertThat(d2.minimum(d1)).isSameInstanceAs(d1);
81     assertThat(d3.minimum(d2)).isSameInstanceAs(d2);
82     assertThat(d2.minimum(d3)).isSameInstanceAs(d2);
83   }
84 
85   @Test
timeCanOverflow()86   public void timeCanOverflow() {
87     ticker.reset(Long.MAX_VALUE);
88     Deadline d = Deadline.after(10, TimeUnit.DAYS, ticker);
89     assertEquals(10, d.timeRemaining(TimeUnit.DAYS));
90     assertTrue(Deadline.after(0, TimeUnit.DAYS, ticker).isBefore(d));
91     assertFalse(d.isExpired());
92 
93     ticker.increment(10, TimeUnit.DAYS);
94     assertTrue(d.isExpired());
95   }
96 
97   @Test
timeCanUnderflow()98   public void timeCanUnderflow() {
99     ticker.reset(Long.MIN_VALUE);
100     Deadline d = Deadline.after(-10, TimeUnit.DAYS, ticker);
101     assertEquals(-10, d.timeRemaining(TimeUnit.DAYS));
102     assertTrue(d.isBefore(Deadline.after(0, TimeUnit.DAYS, ticker)));
103     assertTrue(d.isExpired());
104   }
105 
106   @Test
deadlineClamps()107   public void deadlineClamps() {
108     Deadline d = Deadline.after(-300 * 365, TimeUnit.DAYS, ticker);
109     Deadline d2 = Deadline.after(300 * 365, TimeUnit.DAYS, ticker);
110     assertTrue(d.isBefore(d2));
111 
112     Deadline d3 = Deadline.after(-200 * 365, TimeUnit.DAYS, ticker);
113     // d and d3 are equal
114     assertFalse(d.isBefore(d3));
115     assertFalse(d3.isBefore(d));
116   }
117 
118   @Test
immediateDeadlineIsExpired()119   public void immediateDeadlineIsExpired() {
120     Deadline deadline = Deadline.after(0, TimeUnit.SECONDS, ticker);
121     assertTrue(deadline.isExpired());
122   }
123 
124   @Test
shortDeadlineEventuallyExpires()125   public void shortDeadlineEventuallyExpires() throws Exception {
126     Deadline d = Deadline.after(100, TimeUnit.MILLISECONDS, ticker);
127     assertTrue(d.timeRemaining(TimeUnit.NANOSECONDS) > 0);
128     assertFalse(d.isExpired());
129     ticker.increment(101, TimeUnit.MILLISECONDS);
130 
131     assertTrue(d.isExpired());
132     assertEquals(-1, d.timeRemaining(TimeUnit.MILLISECONDS));
133   }
134 
135   @Test
deadlineMatchesLongValue()136   public void deadlineMatchesLongValue() {
137     assertEquals(10, Deadline.after(10, TimeUnit.MINUTES, ticker).timeRemaining(TimeUnit.MINUTES));
138   }
139 
140   @Test
pastDeadlineIsExpired()141   public void pastDeadlineIsExpired() {
142     Deadline d = Deadline.after(-1, TimeUnit.SECONDS, ticker);
143     assertTrue(d.isExpired());
144     assertEquals(-1000, d.timeRemaining(TimeUnit.MILLISECONDS));
145   }
146 
147   @Test
deadlineDoesNotOverflowOrUnderflow()148   public void deadlineDoesNotOverflowOrUnderflow() {
149     Deadline after = Deadline.after(Long.MAX_VALUE, TimeUnit.NANOSECONDS, ticker);
150     assertFalse(after.isExpired());
151 
152     Deadline before = Deadline.after(-Long.MAX_VALUE, TimeUnit.NANOSECONDS, ticker);
153     assertTrue(before.isExpired());
154 
155     assertTrue(before.isBefore(after));
156   }
157 
158   @Test
beforeExpiredDeadlineIsExpired()159   public void beforeExpiredDeadlineIsExpired() {
160     Deadline base = Deadline.after(0, TimeUnit.SECONDS, ticker);
161     assertTrue(base.isExpired());
162     assertTrue(base.offset(-1, TimeUnit.SECONDS).isExpired());
163   }
164 
165   @Test
beforeNotExpiredDeadlineMayBeExpired()166   public void beforeNotExpiredDeadlineMayBeExpired() {
167     Deadline base = Deadline.after(10, TimeUnit.SECONDS, ticker);
168     assertFalse(base.isExpired());
169     assertFalse(base.offset(-1, TimeUnit.SECONDS).isExpired());
170     assertTrue(base.offset(-11, TimeUnit.SECONDS).isExpired());
171   }
172 
173   @Test
afterExpiredDeadlineMayBeExpired()174   public void afterExpiredDeadlineMayBeExpired() {
175     Deadline base = Deadline.after(-10, TimeUnit.SECONDS, ticker);
176     assertTrue(base.isExpired());
177     assertTrue(base.offset(1, TimeUnit.SECONDS).isExpired());
178     assertFalse(base.offset(11, TimeUnit.SECONDS).isExpired());
179   }
180 
181   @Test
zeroOffsetIsSameDeadline()182   public void zeroOffsetIsSameDeadline() {
183     Deadline base = Deadline.after(0, TimeUnit.SECONDS, ticker);
184     assertSame(base, base.offset(0, TimeUnit.SECONDS));
185   }
186 
187   @Test
runOnEventualExpirationIsExecuted()188   public void runOnEventualExpirationIsExecuted() throws Exception {
189     Deadline base = Deadline.after(50, TimeUnit.MICROSECONDS, ticker);
190     ScheduledExecutorService mockScheduler = mock(ScheduledExecutorService.class);
191     final AtomicBoolean executed = new AtomicBoolean();
192     Future<?> unused = base.runOnExpiration(
193         new Runnable() {
194           @Override
195           public void run() {
196             executed.set(true);
197           }
198         }, mockScheduler);
199     assertFalse(executed.get());
200     ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
201     verify(mockScheduler).schedule(runnableCaptor.capture(), eq(50000L), eq(TimeUnit.NANOSECONDS));
202     runnableCaptor.getValue().run();
203     assertTrue(executed.get());
204   }
205 
206   @Test
runOnAlreadyExpiredIsExecutedOnExecutor()207   public void runOnAlreadyExpiredIsExecutedOnExecutor() throws Exception {
208     Deadline base = Deadline.after(0, TimeUnit.MICROSECONDS, ticker);
209     ScheduledExecutorService mockScheduler = mock(ScheduledExecutorService.class);
210     final AtomicBoolean executed = new AtomicBoolean();
211     Future<?> unused = base.runOnExpiration(
212         new Runnable() {
213           @Override
214           public void run() {
215             executed.set(true);
216           }
217         }, mockScheduler);
218     assertFalse(executed.get());
219     ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
220     verify(mockScheduler).schedule(runnableCaptor.capture(), eq(0L), eq(TimeUnit.NANOSECONDS));
221     runnableCaptor.getValue().run();
222     assertTrue(executed.get());
223   }
224 
225   @Test
toString_systemTickerNotShown()226   public void toString_systemTickerNotShown() {
227     Deadline d = Deadline.after(0, TimeUnit.MILLISECONDS);
228     assertThat(d.toString()).endsWith("s from now");
229   }
230 
231   @Test
toString_exact()232   public void toString_exact() {
233     Deadline d = Deadline.after(0, TimeUnit.MILLISECONDS, ticker);
234     assertEquals("0s from now (ticker=FAKE_TICKER)", d.toString());
235   }
236 
237   @Test
toString_after()238   public void toString_after() {
239     Deadline d;
240 
241     d = Deadline.after(-1, TimeUnit.MINUTES, ticker);
242     assertEquals("-60s from now (ticker=FAKE_TICKER)", d.toString());
243     d = Deadline.after(-1, TimeUnit.MILLISECONDS, ticker);
244     assertEquals("-0.001000000s from now (ticker=FAKE_TICKER)", d.toString());
245     d = Deadline.after(-500, TimeUnit.MILLISECONDS, ticker);
246     assertEquals("-0.500000000s from now (ticker=FAKE_TICKER)", d.toString());
247     d = Deadline.after(-1000, TimeUnit.MILLISECONDS, ticker);
248     assertEquals("-1s from now (ticker=FAKE_TICKER)", d.toString());
249     d = Deadline.after(-1500, TimeUnit.MILLISECONDS, ticker);
250     assertEquals("-1.500000000s from now (ticker=FAKE_TICKER)", d.toString());
251     d = Deadline.after(-1023456789, TimeUnit.NANOSECONDS, ticker);
252     assertEquals("-1.023456789s from now (ticker=FAKE_TICKER)", d.toString());
253   }
254 
255   @Test
compareTo_greater()256   public void compareTo_greater() {
257     Deadline d1 = Deadline.after(10, TimeUnit.SECONDS, ticker);
258     ticker.increment(1, TimeUnit.NANOSECONDS);
259     Deadline d2 = Deadline.after(10, TimeUnit.SECONDS, ticker);
260     Truth.assertThat(d2).isGreaterThan(d1);
261   }
262 
263   @Test
compareTo_less()264   public void compareTo_less() {
265     Deadline d1 = Deadline.after(10, TimeUnit.SECONDS, ticker);
266     ticker.increment(1, TimeUnit.NANOSECONDS);
267     Deadline d2 = Deadline.after(10, TimeUnit.SECONDS, ticker);
268     Truth.assertThat(d1).isLessThan(d2);
269   }
270 
271   @Test
compareTo_same()272   public void compareTo_same() {
273     Deadline d1 = Deadline.after(10, TimeUnit.SECONDS, ticker);
274     Deadline d2 = Deadline.after(10, TimeUnit.SECONDS, ticker);
275     Truth.assertThat(d1).isEquivalentAccordingToCompareTo(d2);
276   }
277 
278   @Test
tickersDontMatch()279   public void tickersDontMatch() {
280     Deadline d1 = Deadline.after(10, TimeUnit.SECONDS);
281     Deadline d2 = Deadline.after(10, TimeUnit.SECONDS, ticker);
282     boolean success = false;
283     try {
284       d1.compareTo(d2);
285       success = true;
286     } catch (AssertionError e) {
287       // Expected
288     }
289     assertFalse(success);
290 
291     try {
292       d1.minimum(d2);
293       success = true;
294     } catch (AssertionError e) {
295       // Expected
296     }
297     assertFalse(success);
298 
299     try {
300       d1.isBefore(d2);
301       success = true;
302     } catch (AssertionError e) {
303       // Expected
304     }
305     assertFalse(success);
306   }
307 
308   @Test
toString_before()309   public void toString_before() {
310     Deadline d = Deadline.after(12, TimeUnit.MICROSECONDS, ticker);
311     assertEquals("0.000012000s from now (ticker=FAKE_TICKER)", d.toString());
312   }
313 
314   @Test
equality()315   public void equality() {
316     final Deadline d1 = Deadline.after(12, TimeUnit.MICROSECONDS, ticker);
317     final Deadline d2 = Deadline.after(12, TimeUnit.MICROSECONDS, ticker);
318     final Deadline d3 = Deadline.after(12, TimeUnit.MICROSECONDS, new FakeTicker());
319     final Deadline d4 = Deadline.after(10, TimeUnit.MICROSECONDS, ticker);
320 
321     new EqualsTester()
322             .addEqualityGroup(d1, d2)
323             .addEqualityGroup(d3)
324             .addEqualityGroup(d4)
325             .testEquals();
326   }
327 
328   private static class FakeTicker extends Deadline.Ticker {
329     private long time;
330 
331     @Override
nanoTime()332     public long nanoTime() {
333       return time;
334     }
335 
reset(long time)336     public void reset(long time) {
337       this.time = time;
338     }
339 
increment(long period, TimeUnit unit)340     public void increment(long period, TimeUnit unit) {
341       if (period < 0) {
342         throw new IllegalArgumentException();
343       }
344       this.time += unit.toNanos(period);
345     }
346 
347     @Override
toString()348     public String toString() {
349       return "FAKE_TICKER";
350     }
351   }
352 }
353