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