/* * Copyright 2009 James Abley * * 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.eternus.ratelimit; import static org.junit.Assert.*; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.junit.Test; /** * Tests for {@link FixedBucket} which are defined to use a variety of {@link TokenStore} implementations. * * @author jabley * */ public abstract class FixedBucketTests { @Test public void sequentialFixedBucketAccess() { FixedBucket rateLimiter = new FixedBucket(); int allowedRequests = 1; rateLimiter.setAllowedRequests(allowedRequests); rateLimiter.setTokenStore(createTokenStore()); rateLimiter.init(); RateLimiterKey key = new RateLimiterKey(); Token token = rateLimiter.getToken(key); assertTrue("We have a usable token back for the first request", token.isUsable()); token = rateLimiter.getToken(key); assertFalse("The second token is not usable, since we assume that the two token" + " accesses take less than a second to perform", token.isUsable()); } @Test public void canDoReasonableNumberOfTokenChecksPerSecond() throws Exception { FixedBucket rateLimiter = new FixedBucket(); int allowedRequests = 50000; rateLimiter.setAllowedRequests(allowedRequests); rateLimiter.setTokenStore(createTokenStore()); rateLimiter.init(); RateLimiterKey key = new RateLimiterKey(); Token token; for (int i = 0, n = allowedRequests; i < n; ++i) { token = rateLimiter.getToken(key); assertTrue("We have a usable token back for the first request", token.isUsable()); } token = rateLimiter.getToken(key); assertFalse("The current token is not usable, since we assume that the " + allowedRequests + " token" + " accesses take less than a second to perform", token.isUsable()); } @Test public void multipleClientsCanAccessWithoutBlocking() throws Exception { final FixedBucket rateLimiter = new FixedBucket(); int allowedRequests = 200; rateLimiter.setAllowedRequests(allowedRequests); rateLimiter.setTokenStore(createTokenStore()); rateLimiter.init(); final RateLimiterKey key = new RateLimiterKey(); int clientCount = allowedRequests; Runnable[] clients = new Runnable[clientCount]; final boolean[] isUsable = new boolean[clientCount]; final CountDownLatch startGate = new CountDownLatch(1); final CountDownLatch endGate = new CountDownLatch(clientCount); for (int i = 0, n = isUsable.length; i < n; ++i) { final int j = i; clients[j] = new Runnable() { /** * {@inheritDoc} */ public void run() { try { startGate.await(); isUsable[j] = rateLimiter.getToken(key).isUsable(); } catch (InterruptedException e) { e.printStackTrace(); } finally { endGate.countDown(); } } }; } ExecutorService executor = Executors.newFixedThreadPool(clientCount); for (Runnable runnable : clients) { executor.execute(runnable); } startGate.countDown(); endGate.await(); for (boolean b : isUsable) { assertTrue("Token was usable", b); } } @Test public void expiryOfTokensIsSupported() throws Exception { FixedBucket rateLimiter = new FixedBucket(); int allowedRequests = 50; rateLimiter.setAllowedRequests(allowedRequests); rateLimiter.setTokenStore(createTokenStore()); rateLimiter.setDuration(1); rateLimiter.init(); RateLimiterKey key = new RateLimiterKey(); Token token = rateLimiter.getToken(key); assertTrue("We have a usable token back for the first request", token.isUsable()); // Allow the token to expire Thread.sleep(1001); token = rateLimiter.getToken(key); assertTrue("We have a usable token back for the second request", token.isUsable()); } /** * Factory Method to return a {@link TokenStore} for test usage. * * @return a non-null {@link TokenStore} */ protected abstract TokenStore createTokenStore(); }