// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.stats;
import com.twitter.common.util.Clock;
import com.twitter.common.util.testing.FakeClock;
import org.easymock.IMocksControl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicLong;
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
/**
* @author William Farner
*/
public class RateTest {
private static final int ONE_SEC = 1000;
private static final double EPSILON = 1E-6;
private IMocksControl control;
private Clock clock;
private Stat<Integer> input;
@Before
@SuppressWarnings("unchecked")
public void setUp() {
control = createControl();
clock = new FakeClock();
input = control.createMock(Stat.class);
}
@After
public void verify() {
Stats.flush();
control.verify();
}
@Test
public void testInputsRegistered() {
expect(input.getName()).andReturn("test");
expectLastCall().atLeastOnce();
control.replay();
Rate.of(input);
assertNotNull(Stats.getVariable("test"));
}
@Test
public void testNoHistory() throws Exception {
expectCalls(0);
control.replay();
assertResults();
}
@Test
public void testFlat() throws Exception {
expectCalls(10, 10);
control.replay();
assertResults(0);
}
@Test
public void testFixedRate() throws Exception {
expectCalls(10, 20, 30, 40);
control.replay();
assertResults(10, 10, 10);
}
@Test
public void testVariableRate() throws Exception {
expectCalls(10, 20, 50, 150);
control.replay();
assertResults(10, 30, 100);
}
@Test
public void testVariableRateAtomicLong() throws Exception {
AtomicLong value = new AtomicLong();
Rate<AtomicLong> rate = Rate.of("test", value).withClock(clock).build();
clock.waitFor(ONE_SEC);
value.set(10);
assertEquals(0d, rate.sample(), EPSILON);
clock.waitFor(ONE_SEC);
value.set(20);
assertEquals(10d, rate.sample(), EPSILON);
clock.waitFor(ONE_SEC);
value.set(50);
assertEquals(30d, rate.sample(), EPSILON);
clock.waitFor(ONE_SEC);
value.set(100);
assertEquals(50d, rate.sample(), EPSILON);
control.replay();
}
@Test
public void testNegativeRate() throws Exception {
expectCalls(40, 30, 20, 10);
control.replay();
assertResults(-10, -10, -10);
}
@Test
public void testZeroDelta() throws Exception {
expectCalls(10, 10, 10);
control.replay();
assertResults(0, 0);
}
@Test
public void testLongWindow() throws Exception {
expectCalls(10, 10, 0, 10, 10);
control.replay();
assertResults(Rate.of(input).withWindowSize(3).withClock(clock).build(), 0, -5, 0, 0);
}
@Test
public void testRateOfRate() throws Exception {
expectCalls(10, 20, 30, 40, 50, 60);
control.replay();
Rate<Integer> rate = Rate.of(input).withClock(clock).build();
Rate<Double> rateOfRate = Rate.of(rate).withClock(clock).build();
assertThat(rate.sample(), is(0d));
assertThat(rateOfRate.sample(), is(0d));
clock.waitFor(ONE_SEC);
assertThat(rate.sample(), is(10d));
assertThat(rateOfRate.sample(), is(10d));
clock.waitFor(ONE_SEC);
assertThat(rate.sample(), is(10d));
assertThat(rateOfRate.sample(), is(0d));
clock.waitFor(ONE_SEC);
assertThat(rate.sample(), is(10d));
assertThat(rateOfRate.sample(), is(0d));
clock.waitFor(ONE_SEC);
assertThat(rate.sample(), is(10d));
assertThat(rateOfRate.sample(), is(0d));
clock.waitFor(ONE_SEC);
assertThat(rate.sample(), is(10d));
assertThat(rateOfRate.sample(), is(0d));
}
@Test
public void testScaleFactor() throws Exception {
expectCalls(10, 20, 30, 40);
control.replay();
assertResults(Rate.of(input).withClock(clock).withScaleFactor(10).build(), 100, 100, 100);
}
@Test
public void testFractionalScaleFactor() throws Exception {
expectCalls(10, 20, 30, 40);
control.replay();
assertResults(Rate.of(input).withClock(clock).withScaleFactor(0.1).build(), 1, 1, 1);
}
private void expectCalls(int... samples) {
expect(input.getName()).andReturn("test");
expectLastCall().atLeastOnce();
for (int sample : samples) {
expect(input.read()).andReturn(sample);
}
}
private void assertResults(double... results) throws Exception {
assertResults(Rate.of(input).withClock(clock).build(), results);
}
private void assertResults(Rate<Integer> rate, double... results) throws Exception {
// First result is always zero.
assertEquals(0d, rate.sample(), EPSILON);
clock.waitFor(ONE_SEC);
for (double result : results) {
assertEquals(result, rate.sample(), EPSILON);
clock.waitFor(ONE_SEC);
}
}
}