/* TestRollingCounter.java
*
* Copyright 2009-2015 Comcast Interactive Media, LLC.
*
* 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 org.fishwife.jrugged;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
public final class TestWindowedEventCounter {
private WindowedEventCounter impl;
private StoppedClock clock = new StoppedClock();
private static int CAPACITY = 3;
private static long WINDOW_MILLIS = 5L;
@Before
public void setUp() {
impl = new WindowedEventCounter(CAPACITY, WINDOW_MILLIS);
clock.currentTimeMillis = System.currentTimeMillis();
impl.setClock(clock);
// several tests depend on this assumption
assertTrue(WINDOW_MILLIS > CAPACITY);
}
// constructor tests
@Test
public void testConstructor() {
assertEquals(CAPACITY, impl.getCapacity());
assertEquals(WINDOW_MILLIS, impl.getWindowMillis());
}
@Test
public void testConstructorThrowsExceptionOnBadCapacity() {
try {
new WindowedEventCounter(0, WINDOW_MILLIS);
fail("constructor should have thrown IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// this is expected. ignore and let test pass.
}
}
@Test
public void testConstructorThrowsExceptionOnBadWindowMillis() {
try {
new WindowedEventCounter(CAPACITY, 0);
fail("constructor should have thrown IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// this is expected. ignore and let test pass.
}
}
@Test
public void testStartsEmpty() {
assertEquals(0, impl.tally());
}
@Test
public void testCountsToCapacity() {
for (int i = 1; i <= CAPACITY; i++) {
impl.mark();
clock.currentTimeMillis = clock.currentTimeMillis + 1;
assertEquals(i, impl.tally());
}
}
@Test
public void testCountsToCapacityOnOverflow() {
for (int i = 0; i < (CAPACITY * 2); i++) {
clock.currentTimeMillis = clock.currentTimeMillis + 1;
impl.mark();
}
assertEquals(CAPACITY, impl.tally());
}
@Test
public void testRollingExpiration() {
// empty at t0
long t0 = clock.currentTimeMillis;
/*
* fill 'er up, marking once per milli (first event is at t1, second
* event at t2...)
*/
for (int i = 1; i <= CAPACITY; i++) {
clock.currentTimeMillis = t0 + i;
impl.mark();
}
/*
* represents that last time that all the events should still be
* in-window.
*/
clock.currentTimeMillis = t0 + 1 + WINDOW_MILLIS;
int expectedCount = CAPACITY;
assertEquals(CAPACITY, impl.tally());
// the tally count should drain at a rate one per milli now
for (int j = 1; j <= CAPACITY; j++) {
clock.currentTimeMillis++;
expectedCount--;
assertEquals(expectedCount, impl.tally());
}
clock.currentTimeMillis++;
// we should be empty now
assertEquals(0, impl.tally());
}
@Test
public void testReducingWindowDecreasesTally() throws Exception {
// empty at t0
long t0 = clock.currentTimeMillis;
/*
* fill 'er up, marking once per milli (first event is at t1, second
* event at t2...)
*/
for (int i = 1; i <= CAPACITY; i++) {
clock.currentTimeMillis = t0 + i;
impl.mark();
}
// Move time to 1 MS past the last entry.
clock.currentTimeMillis = clock.currentTimeMillis + 1;
impl.setWindowMillis(1);
assertEquals(1, impl.tally());
}
@Test
public void testReducingCapacityDecreasesTally() {
// fill up to capacity
for (int i = 1; i <= CAPACITY; i++) {
impl.mark();
}
assertEquals(CAPACITY, impl.tally());
// reduce capacity
impl.setCapacity(CAPACITY - 1);
assertEquals(CAPACITY - 1, impl.tally());
}
@Test
public void testIncreasingCapacity() {
// fill up to capacity
for (int i = 1; i <= CAPACITY; i++) {
impl.mark();
}
assertEquals(CAPACITY, impl.tally());
impl.setCapacity(CAPACITY + 1);
assertEquals(CAPACITY, impl.tally());
}
@Test
public void testSettingBadCapacityThrowsException() {
try {
impl.setCapacity(0);
fail("should have thrown IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// this is expected. ignore and let test pass.
}
}
@Test
public void testSettingBadWindowMillisThrowsException() {
try {
impl.setWindowMillis(0);
fail("should have thrown IllegalArgumentException");
} catch (IllegalArgumentException iae) {
// this is expected. ignore and let test pass.
}
}
public class StoppedClock implements Clock {
public long currentTimeMillis;
public long currentTimeMillis() {
return currentTimeMillis;
}
}
}