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