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