1 /* 2 * Copyright (C) 2012 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 java.lang.reflect.Modifier.isStatic; 20 import static java.util.concurrent.TimeUnit.MICROSECONDS; 21 import static java.util.concurrent.TimeUnit.MILLISECONDS; 22 import static java.util.concurrent.TimeUnit.NANOSECONDS; 23 import static java.util.concurrent.TimeUnit.SECONDS; 24 import static org.junit.Assert.assertThrows; 25 26 import com.google.common.collect.ImmutableClassToInstanceMap; 27 import com.google.common.collect.ImmutableSet; 28 import com.google.common.collect.Lists; 29 import com.google.common.testing.NullPointerTester; 30 import com.google.common.testing.NullPointerTester.Visibility; 31 import com.google.common.util.concurrent.RateLimiter.SleepingStopwatch; 32 import java.lang.reflect.Method; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.Locale; 36 import java.util.Random; 37 import java.util.concurrent.TimeUnit; 38 import junit.framework.TestCase; 39 import org.mockito.Mockito; 40 41 /** 42 * Tests for RateLimiter. 43 * 44 * @author Dimitris Andreou 45 */ 46 public class RateLimiterTest extends TestCase { 47 private static final double EPSILON = 1e-8; 48 49 private final FakeStopwatch stopwatch = new FakeStopwatch(); 50 testSimple()51 public void testSimple() { 52 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 53 limiter.acquire(); // R0.00, since it's the first request 54 limiter.acquire(); // R0.20 55 limiter.acquire(); // R0.20 56 assertEvents("R0.00", "R0.20", "R0.20"); 57 } 58 testImmediateTryAcquire()59 public void testImmediateTryAcquire() { 60 RateLimiter r = RateLimiter.create(1); 61 assertTrue("Unable to acquire initial permit", r.tryAcquire()); 62 assertFalse("Capable of acquiring secondary permit", r.tryAcquire()); 63 } 64 testDoubleMinValueCanAcquireExactlyOnce()65 public void testDoubleMinValueCanAcquireExactlyOnce() { 66 RateLimiter r = RateLimiter.create(Double.MIN_VALUE, stopwatch); 67 assertTrue("Unable to acquire initial permit", r.tryAcquire()); 68 assertFalse("Capable of acquiring an additional permit", r.tryAcquire()); 69 stopwatch.sleepMillis(Integer.MAX_VALUE); 70 assertFalse("Capable of acquiring an additional permit after sleeping", r.tryAcquire()); 71 } 72 testSimpleRateUpdate()73 public void testSimpleRateUpdate() { 74 RateLimiter limiter = RateLimiter.create(5.0, 5, SECONDS); 75 assertEquals(5.0, limiter.getRate()); 76 limiter.setRate(10.0); 77 assertEquals(10.0, limiter.getRate()); 78 79 assertThrows(IllegalArgumentException.class, () -> limiter.setRate(0.0)); 80 assertThrows(IllegalArgumentException.class, () -> limiter.setRate(-10.0)); 81 } 82 testAcquireParameterValidation()83 public void testAcquireParameterValidation() { 84 RateLimiter limiter = RateLimiter.create(999); 85 assertThrows(IllegalArgumentException.class, () -> limiter.acquire(0)); 86 assertThrows(IllegalArgumentException.class, () -> limiter.acquire(-1)); 87 assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(0)); 88 assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(-1)); 89 assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(0, 1, SECONDS)); 90 assertThrows(IllegalArgumentException.class, () -> limiter.tryAcquire(-1, 1, SECONDS)); 91 } 92 testSimpleWithWait()93 public void testSimpleWithWait() { 94 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 95 limiter.acquire(); // R0.00 96 stopwatch.sleepMillis(200); // U0.20, we are ready for the next request... 97 limiter.acquire(); // R0.00, ...which is granted immediately 98 limiter.acquire(); // R0.20 99 assertEvents("R0.00", "U0.20", "R0.00", "R0.20"); 100 } 101 testSimpleAcquireReturnValues()102 public void testSimpleAcquireReturnValues() { 103 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 104 assertEquals(0.0, limiter.acquire(), EPSILON); // R0.00 105 stopwatch.sleepMillis(200); // U0.20, we are ready for the next request... 106 assertEquals(0.0, limiter.acquire(), EPSILON); // R0.00, ...which is granted immediately 107 assertEquals(0.2, limiter.acquire(), EPSILON); // R0.20 108 assertEvents("R0.00", "U0.20", "R0.00", "R0.20"); 109 } 110 testSimpleAcquireEarliestAvailableIsInPast()111 public void testSimpleAcquireEarliestAvailableIsInPast() { 112 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 113 assertEquals(0.0, limiter.acquire(), EPSILON); 114 stopwatch.sleepMillis(400); 115 assertEquals(0.0, limiter.acquire(), EPSILON); 116 assertEquals(0.0, limiter.acquire(), EPSILON); 117 assertEquals(0.2, limiter.acquire(), EPSILON); 118 } 119 testOneSecondBurst()120 public void testOneSecondBurst() { 121 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 122 stopwatch.sleepMillis(1000); // max capacity reached 123 stopwatch.sleepMillis(1000); // this makes no difference 124 limiter.acquire(1); // R0.00, since it's the first request 125 126 limiter.acquire(1); // R0.00, from capacity 127 limiter.acquire(3); // R0.00, from capacity 128 limiter.acquire(1); // R0.00, concluding a burst of 5 permits 129 130 limiter.acquire(); // R0.20, capacity exhausted 131 assertEvents( 132 "U1.00", "U1.00", "R0.00", "R0.00", "R0.00", "R0.00", // first request and burst 133 "R0.20"); 134 } 135 testCreateWarmupParameterValidation()136 public void testCreateWarmupParameterValidation() { 137 RateLimiter unused; 138 unused = RateLimiter.create(1.0, 1, NANOSECONDS); 139 unused = RateLimiter.create(1.0, 0, NANOSECONDS); 140 141 assertThrows(IllegalArgumentException.class, () -> RateLimiter.create(0.0, 1, NANOSECONDS)); 142 143 assertThrows(IllegalArgumentException.class, () -> RateLimiter.create(1.0, -1, NANOSECONDS)); 144 } 145 146 @AndroidIncompatible // difference in String.format rounding? testWarmUp()147 public void testWarmUp() { 148 RateLimiter limiter = RateLimiter.create(2.0, 4000, MILLISECONDS, 3.0, stopwatch); 149 for (int i = 0; i < 8; i++) { 150 limiter.acquire(); // #1 151 } 152 stopwatch.sleepMillis(500); // #2: to repay for the last acquire 153 stopwatch.sleepMillis(4000); // #3: becomes cold again 154 for (int i = 0; i < 8; i++) { 155 limiter.acquire(); // // #4 156 } 157 stopwatch.sleepMillis(500); // #5: to repay for the last acquire 158 stopwatch.sleepMillis(2000); // #6: didn't get cold! It would take another 2 seconds to go cold 159 for (int i = 0; i < 8; i++) { 160 limiter.acquire(); // #7 161 } 162 assertEvents( 163 "R0.00, R1.38, R1.13, R0.88, R0.63, R0.50, R0.50, R0.50", // #1 164 "U0.50", // #2 165 "U4.00", // #3 166 "R0.00, R1.38, R1.13, R0.88, R0.63, R0.50, R0.50, R0.50", // #4 167 "U0.50", // #5 168 "U2.00", // #6 169 "R0.00, R0.50, R0.50, R0.50, R0.50, R0.50, R0.50, R0.50"); // #7 170 } 171 testWarmUpWithColdFactor()172 public void testWarmUpWithColdFactor() { 173 RateLimiter limiter = RateLimiter.create(5.0, 4000, MILLISECONDS, 10.0, stopwatch); 174 for (int i = 0; i < 8; i++) { 175 limiter.acquire(); // #1 176 } 177 stopwatch.sleepMillis(200); // #2: to repay for the last acquire 178 stopwatch.sleepMillis(4000); // #3: becomes cold again 179 for (int i = 0; i < 8; i++) { 180 limiter.acquire(); // // #4 181 } 182 stopwatch.sleepMillis(200); // #5: to repay for the last acquire 183 stopwatch.sleepMillis(1000); // #6: still warm! It would take another 3 seconds to go cold 184 for (int i = 0; i < 8; i++) { 185 limiter.acquire(); // #7 186 } 187 assertEvents( 188 "R0.00, R1.75, R1.26, R0.76, R0.30, R0.20, R0.20, R0.20", // #1 189 "U0.20", // #2 190 "U4.00", // #3 191 "R0.00, R1.75, R1.26, R0.76, R0.30, R0.20, R0.20, R0.20", // #4 192 "U0.20", // #5 193 "U1.00", // #6 194 "R0.00, R0.20, R0.20, R0.20, R0.20, R0.20, R0.20, R0.20"); // #7 195 } 196 testWarmUpWithColdFactor1()197 public void testWarmUpWithColdFactor1() { 198 RateLimiter limiter = RateLimiter.create(5.0, 4000, MILLISECONDS, 1.0, stopwatch); 199 for (int i = 0; i < 8; i++) { 200 limiter.acquire(); // #1 201 } 202 stopwatch.sleepMillis(340); // #2 203 for (int i = 0; i < 8; i++) { 204 limiter.acquire(); // #3 205 } 206 assertEvents( 207 "R0.00, R0.20, R0.20, R0.20, R0.20, R0.20, R0.20, R0.20", // #1 208 "U0.34", // #2 209 "R0.00, R0.20, R0.20, R0.20, R0.20, R0.20, R0.20, R0.20"); // #3 210 } 211 212 @AndroidIncompatible // difference in String.format rounding? testWarmUpAndUpdate()213 public void testWarmUpAndUpdate() { 214 RateLimiter limiter = RateLimiter.create(2.0, 4000, MILLISECONDS, 3.0, stopwatch); 215 for (int i = 0; i < 8; i++) { 216 limiter.acquire(); // // #1 217 } 218 stopwatch.sleepMillis(4500); // #2: back to cold state (warmup period + repay last acquire) 219 for (int i = 0; i < 3; i++) { // only three steps, we're somewhere in the warmup period 220 limiter.acquire(); // #3 221 } 222 223 limiter.setRate(4.0); // double the rate! 224 limiter.acquire(); // #4, we repay the debt of the last acquire (imposed by the old rate) 225 for (int i = 0; i < 4; i++) { 226 limiter.acquire(); // #5 227 } 228 stopwatch.sleepMillis(4250); // #6, back to cold state (warmup period + repay last acquire) 229 for (int i = 0; i < 11; i++) { 230 limiter.acquire(); // #7, showing off the warmup starting from totally cold 231 } 232 233 // make sure the areas (times) remain the same, while permits are different 234 assertEvents( 235 "R0.00, R1.38, R1.13, R0.88, R0.63, R0.50, R0.50, R0.50", // #1 236 "U4.50", // #2 237 "R0.00, R1.38, R1.13", // #3, after that the rate changes 238 "R0.88", // #4, this is what the throttling would be with the old rate 239 "R0.34, R0.28, R0.25, R0.25", // #5 240 "U4.25", // #6 241 "R0.00, R0.72, R0.66, R0.59, R0.53, R0.47, R0.41", // #7 242 "R0.34, R0.28, R0.25, R0.25"); // #7 (cont.), note, this matches #5 243 } 244 testWarmUpAndUpdateWithColdFactor()245 public void testWarmUpAndUpdateWithColdFactor() { 246 RateLimiter limiter = RateLimiter.create(5.0, 4000, MILLISECONDS, 10.0, stopwatch); 247 for (int i = 0; i < 8; i++) { 248 limiter.acquire(); // #1 249 } 250 stopwatch.sleepMillis(4200); // #2: back to cold state (warmup period + repay last acquire) 251 for (int i = 0; i < 3; i++) { // only three steps, we're somewhere in the warmup period 252 limiter.acquire(); // #3 253 } 254 255 limiter.setRate(10.0); // double the rate! 256 limiter.acquire(); // #4, we repay the debt of the last acquire (imposed by the old rate) 257 for (int i = 0; i < 4; i++) { 258 limiter.acquire(); // #5 259 } 260 stopwatch.sleepMillis(4100); // #6, back to cold state (warmup period + repay last acquire) 261 for (int i = 0; i < 11; i++) { 262 limiter.acquire(); // #7, showing off the warmup starting from totally cold 263 } 264 265 // make sure the areas (times) remain the same, while permits are different 266 assertEvents( 267 "R0.00, R1.75, R1.26, R0.76, R0.30, R0.20, R0.20, R0.20", // #1 268 "U4.20", // #2 269 "R0.00, R1.75, R1.26", // #3, after that the rate changes 270 "R0.76", // #4, this is what the throttling would be with the old rate 271 "R0.20, R0.10, R0.10, R0.10", // #5 272 "U4.10", // #6 273 "R0.00, R0.94, R0.81, R0.69, R0.57, R0.44, R0.32", // #7 274 "R0.20, R0.10, R0.10, R0.10"); // #7 (cont.), note, this matches #5 275 } 276 testBurstyAndUpdate()277 public void testBurstyAndUpdate() { 278 RateLimiter rateLimiter = RateLimiter.create(1.0, stopwatch); 279 rateLimiter.acquire(1); // no wait 280 rateLimiter.acquire(1); // R1.00, to repay previous 281 282 rateLimiter.setRate(2.0); // update the rate! 283 284 rateLimiter.acquire(1); // R1.00, to repay previous (the previous was under the old rate!) 285 rateLimiter.acquire(2); // R0.50, to repay previous (now the rate takes effect) 286 rateLimiter.acquire(4); // R1.00, to repay previous 287 rateLimiter.acquire(1); // R2.00, to repay previous 288 assertEvents("R0.00", "R1.00", "R1.00", "R0.50", "R1.00", "R2.00"); 289 } 290 testTryAcquire_noWaitAllowed()291 public void testTryAcquire_noWaitAllowed() { 292 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 293 assertTrue(limiter.tryAcquire(0, SECONDS)); 294 assertFalse(limiter.tryAcquire(0, SECONDS)); 295 assertFalse(limiter.tryAcquire(0, SECONDS)); 296 stopwatch.sleepMillis(100); 297 assertFalse(limiter.tryAcquire(0, SECONDS)); 298 } 299 testTryAcquire_someWaitAllowed()300 public void testTryAcquire_someWaitAllowed() { 301 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 302 assertTrue(limiter.tryAcquire(0, SECONDS)); 303 assertTrue(limiter.tryAcquire(200, MILLISECONDS)); 304 assertFalse(limiter.tryAcquire(100, MILLISECONDS)); 305 stopwatch.sleepMillis(100); 306 assertTrue(limiter.tryAcquire(100, MILLISECONDS)); 307 } 308 testTryAcquire_overflow()309 public void testTryAcquire_overflow() { 310 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 311 assertTrue(limiter.tryAcquire(0, MICROSECONDS)); 312 stopwatch.sleepMillis(100); 313 assertTrue(limiter.tryAcquire(Long.MAX_VALUE, MICROSECONDS)); 314 } 315 testTryAcquire_negative()316 public void testTryAcquire_negative() { 317 RateLimiter limiter = RateLimiter.create(5.0, stopwatch); 318 assertTrue(limiter.tryAcquire(5, 0, SECONDS)); 319 stopwatch.sleepMillis(900); 320 assertFalse(limiter.tryAcquire(1, Long.MIN_VALUE, SECONDS)); 321 stopwatch.sleepMillis(100); 322 assertTrue(limiter.tryAcquire(1, -1, SECONDS)); 323 } 324 testSimpleWeights()325 public void testSimpleWeights() { 326 RateLimiter rateLimiter = RateLimiter.create(1.0, stopwatch); 327 rateLimiter.acquire(1); // no wait 328 rateLimiter.acquire(1); // R1.00, to repay previous 329 rateLimiter.acquire(2); // R1.00, to repay previous 330 rateLimiter.acquire(4); // R2.00, to repay previous 331 rateLimiter.acquire(8); // R4.00, to repay previous 332 rateLimiter.acquire(1); // R8.00, to repay previous 333 assertEvents("R0.00", "R1.00", "R1.00", "R2.00", "R4.00", "R8.00"); 334 } 335 testInfinity_Bursty()336 public void testInfinity_Bursty() { 337 RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, stopwatch); 338 limiter.acquire(Integer.MAX_VALUE / 4); 339 limiter.acquire(Integer.MAX_VALUE / 2); 340 limiter.acquire(Integer.MAX_VALUE); 341 assertEvents("R0.00", "R0.00", "R0.00"); // no wait, infinite rate! 342 343 limiter.setRate(2.0); 344 limiter.acquire(); 345 limiter.acquire(); 346 limiter.acquire(); 347 limiter.acquire(); 348 limiter.acquire(); 349 assertEvents( 350 "R0.00", // First comes the saved-up burst, which defaults to a 1-second burst (2 requests). 351 "R0.00", "R0.00", // Now comes the free request. 352 "R0.50", // Now it's 0.5 seconds per request. 353 "R0.50"); 354 355 limiter.setRate(Double.POSITIVE_INFINITY); 356 limiter.acquire(); 357 limiter.acquire(); 358 limiter.acquire(); 359 assertEvents("R0.50", "R0.00", "R0.00"); // we repay the last request (.5sec), then back to +oo 360 } 361 362 /** https://code.google.com/p/guava-libraries/issues/detail?id=1791 */ testInfinity_BustyTimeElapsed()363 public void testInfinity_BustyTimeElapsed() { 364 RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, stopwatch); 365 stopwatch.instant += 1000000; 366 limiter.setRate(2.0); 367 for (int i = 0; i < 5; i++) { 368 limiter.acquire(); 369 } 370 assertEvents( 371 "R0.00", // First comes the saved-up burst, which defaults to a 1-second burst (2 requests). 372 "R0.00", "R0.00", // Now comes the free request. 373 "R0.50", // Now it's 0.5 seconds per request. 374 "R0.50"); 375 } 376 testInfinity_WarmUp()377 public void testInfinity_WarmUp() { 378 RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, 10, SECONDS, 3.0, stopwatch); 379 limiter.acquire(Integer.MAX_VALUE / 4); 380 limiter.acquire(Integer.MAX_VALUE / 2); 381 limiter.acquire(Integer.MAX_VALUE); 382 assertEvents("R0.00", "R0.00", "R0.00"); 383 384 limiter.setRate(1.0); 385 limiter.acquire(); 386 limiter.acquire(); 387 limiter.acquire(); 388 assertEvents("R0.00", "R1.00", "R1.00"); 389 390 limiter.setRate(Double.POSITIVE_INFINITY); 391 limiter.acquire(); 392 limiter.acquire(); 393 limiter.acquire(); 394 assertEvents("R1.00", "R0.00", "R0.00"); 395 } 396 testInfinity_WarmUpTimeElapsed()397 public void testInfinity_WarmUpTimeElapsed() { 398 RateLimiter limiter = RateLimiter.create(Double.POSITIVE_INFINITY, 10, SECONDS, 3.0, stopwatch); 399 stopwatch.instant += 1000000; 400 limiter.setRate(1.0); 401 for (int i = 0; i < 5; i++) { 402 limiter.acquire(); 403 } 404 assertEvents("R0.00", "R1.00", "R1.00", "R1.00", "R1.00"); 405 } 406 407 /** 408 * Make sure that bursts can never go above 1-second-worth-of-work for the current rate, even when 409 * we change the rate. 410 */ testWeNeverGetABurstMoreThanOneSec()411 public void testWeNeverGetABurstMoreThanOneSec() { 412 RateLimiter limiter = RateLimiter.create(1.0, stopwatch); 413 int[] rates = {1000, 1, 10, 1000000, 10, 1}; 414 for (int rate : rates) { 415 int oneSecWorthOfWork = rate; 416 stopwatch.sleepMillis(rate * 1000); 417 limiter.setRate(rate); 418 long burst = measureTotalTimeMillis(limiter, oneSecWorthOfWork, new Random()); 419 // we allow one second worth of work to go in a burst (i.e. take less than a second) 420 assertTrue(burst <= 1000); 421 long afterBurst = measureTotalTimeMillis(limiter, oneSecWorthOfWork, new Random()); 422 // but work beyond that must take at least one second 423 assertTrue(afterBurst >= 1000); 424 } 425 } 426 427 /** 428 * This neat test shows that no matter what weights we use in our requests, if we push X amount of 429 * permits in a cool state, where X = rate * timeToCoolDown, and we have specified a 430 * timeToWarmUp() period, it will cost as the prescribed amount of time. E.g., calling 431 * [acquire(5), acquire(1)] takes exactly the same time as [acquire(2), acquire(3), acquire(1)]. 432 */ testTimeToWarmUpIsHonouredEvenWithWeights()433 public void testTimeToWarmUpIsHonouredEvenWithWeights() { 434 Random random = new Random(); 435 int warmupPermits = 10; 436 double[] coldFactorsToTest = {2.0, 3.0, 10.0}; 437 double[] qpsToTest = {4.0, 2.0, 1.0, 0.5, 0.1}; 438 for (int trial = 0; trial < 100; trial++) { 439 for (double coldFactor : coldFactorsToTest) { 440 for (double qps : qpsToTest) { 441 // If warmupPermits = maxPermits - thresholdPermits then 442 // warmupPeriod = (1 + coldFactor) * warmupPermits * stableInterval / 2 443 long warmupMillis = (long) ((1 + coldFactor) * warmupPermits / (2.0 * qps) * 1000.0); 444 RateLimiter rateLimiter = 445 RateLimiter.create(qps, warmupMillis, MILLISECONDS, coldFactor, stopwatch); 446 assertEquals(warmupMillis, measureTotalTimeMillis(rateLimiter, warmupPermits, random)); 447 } 448 } 449 } 450 } 451 testNulls()452 public void testNulls() { 453 NullPointerTester tester = 454 new NullPointerTester() 455 .setDefault(SleepingStopwatch.class, stopwatch) 456 .setDefault(int.class, 1) 457 .setDefault(double.class, 1.0d); 458 tester.testStaticMethods(RateLimiter.class, Visibility.PACKAGE); 459 tester.testInstanceMethods(RateLimiter.create(5.0, stopwatch), Visibility.PACKAGE); 460 } 461 testVerySmallDoubleValues()462 public void testVerySmallDoubleValues() throws Exception { 463 RateLimiter rateLimiter = RateLimiter.create(Double.MIN_VALUE, stopwatch); 464 assertTrue("Should acquire initial permit", rateLimiter.tryAcquire()); 465 assertFalse("Should not acquire additional permit", rateLimiter.tryAcquire()); 466 stopwatch.sleepMillis(5000); 467 assertFalse( 468 "Should not acquire additional permit even after sleeping", rateLimiter.tryAcquire()); 469 } 470 measureTotalTimeMillis(RateLimiter rateLimiter, int permits, Random random)471 private long measureTotalTimeMillis(RateLimiter rateLimiter, int permits, Random random) { 472 long startTime = stopwatch.instant; 473 while (permits > 0) { 474 int nextPermitsToAcquire = Math.max(1, random.nextInt(permits)); 475 permits -= nextPermitsToAcquire; 476 rateLimiter.acquire(nextPermitsToAcquire); 477 } 478 rateLimiter.acquire(1); // to repay for any pending debt 479 return NANOSECONDS.toMillis(stopwatch.instant - startTime); 480 } 481 assertEvents(String... events)482 private void assertEvents(String... events) { 483 assertEquals(Arrays.toString(events), stopwatch.readEventsAndClear()); 484 } 485 486 /** 487 * The stopwatch gathers events and presents them as strings. R0.6 means a delay of 0.6 seconds 488 * caused by the (R)ateLimiter U1.0 means the (U)ser caused the stopwatch to sleep for a second. 489 */ 490 static class FakeStopwatch extends SleepingStopwatch { 491 long instant = 0L; 492 final List<String> events = Lists.newArrayList(); 493 494 @Override readMicros()495 public long readMicros() { 496 return NANOSECONDS.toMicros(instant); 497 } 498 sleepMillis(int millis)499 void sleepMillis(int millis) { 500 sleepMicros("U", MILLISECONDS.toMicros(millis)); 501 } 502 sleepMicros(String caption, long micros)503 void sleepMicros(String caption, long micros) { 504 instant += MICROSECONDS.toNanos(micros); 505 events.add(caption + String.format(Locale.ROOT, "%3.2f", (micros / 1000000.0))); 506 } 507 508 @Override sleepMicrosUninterruptibly(long micros)509 protected void sleepMicrosUninterruptibly(long micros) { 510 sleepMicros("R", micros); 511 } 512 readEventsAndClear()513 String readEventsAndClear() { 514 try { 515 return events.toString(); 516 } finally { 517 events.clear(); 518 } 519 } 520 521 @Override toString()522 public String toString() { 523 return events.toString(); 524 } 525 } 526 527 @AndroidIncompatible // Mockito loses its ability to mock doGetRate as of Android 21 testMockingMockito()528 public void testMockingMockito() throws Exception { 529 RateLimiter mock = Mockito.mock(RateLimiter.class); 530 for (Method method : RateLimiter.class.getMethods()) { 531 if (!isStatic(method.getModifiers()) 532 && !NOT_WORKING_ON_MOCKS.contains(method.getName()) 533 && !method.getDeclaringClass().equals(Object.class)) { 534 method.invoke(mock, arbitraryParameters(method)); 535 } 536 } 537 } 538 arbitraryParameters(Method method)539 private static Object[] arbitraryParameters(Method method) { 540 Class<?>[] parameterTypes = method.getParameterTypes(); 541 Object[] params = new Object[parameterTypes.length]; 542 for (int i = 0; i < parameterTypes.length; i++) { 543 params[i] = PARAMETER_VALUES.get(parameterTypes[i]); 544 } 545 return params; 546 } 547 548 private static final ImmutableSet<String> NOT_WORKING_ON_MOCKS = 549 ImmutableSet.of("latestPermitAgeSec", "latestPermitAge", "setRate", "getAvailablePermits"); 550 551 // We would use ArbitraryInstances, but it returns 0, invalid for many RateLimiter methods. 552 private static final ImmutableClassToInstanceMap<Object> PARAMETER_VALUES = 553 ImmutableClassToInstanceMap.builder() 554 .put(int.class, 1) 555 .put(long.class, 1L) 556 .put(double.class, 1.0) 557 .put(TimeUnit.class, SECONDS) 558 .build(); 559 } 560