/**
* Copyright 2013 Netflix, Inc.
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.netflix.servo.stats;
import org.testng.annotations.Test;
import java.lang.reflect.Field;
import static org.testng.Assert.assertEquals;
public class StatsBufferTest {
static final double[] PERCENTILES = {50.0, 95.0, 99.0, 99.5};
private static final int SIZE = 1000;
StatsBuffer getNoWrap() {
StatsBuffer buffer = new StatsBuffer(SIZE, PERCENTILES);
int max = SIZE / 2;
for (int i = 0; i <= max; ++i) {
buffer.record(i);
}
buffer.computeStats();
return buffer;
}
@Test
public void testMaxNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getMax(), SIZE / 2);
}
@Test
public void testMinNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getMin(), 0);
}
@Test
public void testMeanNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getMean(), SIZE / 4.0);
}
@Test
public void testCountNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getCount(), SIZE / 2 + 1);
}
@Test
public void testTotalNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getTotalTime(), SIZE / 2 * (SIZE / 2 + 1) / 2);
}
@Test
public void testVarianceNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getVariance(), 20916.66667, 1e-4);
}
@Test
public void testStdDevNoWrap() {
StatsBuffer buffer = getNoWrap();
assertEquals(buffer.getStdDev(), 144.62595, 1e-4);
}
@Test
public void testPercentiles50NoWrap() {
StatsBuffer buffer = getNoWrap();
double[] percentiles = buffer.getPercentileValues();
// testNG does not give good errors if we do assertEquals on the two arrays
assertEquals(percentiles[0], 250.5);
}
@Test
public void testPercentiles95NoWrap() {
StatsBuffer buffer = getNoWrap();
double[] percentiles = buffer.getPercentileValues();
assertEquals(percentiles[1], 475.95);
}
@Test
public void testPercentiles99NoWrap() {
StatsBuffer buffer = getNoWrap();
double[] percentiles = buffer.getPercentileValues();
assertEquals(percentiles[2], 495.99);
}
@Test
public void testPercentiles995NoWrap() {
StatsBuffer buffer = getNoWrap();
double[] percentiles = buffer.getPercentileValues();
assertEquals(percentiles[3], 498.495);
}
void assertEmpty(StatsBuffer buffer) {
assertEquals(buffer.getCount(), 0);
assertEquals(buffer.getTotalTime(), 0);
assertEquals(buffer.getMax(), 0);
assertEquals(buffer.getMin(), 0);
// the following values could be NaN
assertEquals(buffer.getMean(), 0.0);
assertEquals(buffer.getVariance(), 0.0);
assertEquals(buffer.getStdDev(), 0.0);
assertEquals(buffer.getPercentileValues()[0], 0.0);
}
@Test
public void testEmptyBuffer() {
StatsBuffer buffer = new StatsBuffer(SIZE, PERCENTILES);
assertEmpty(buffer);
buffer.computeStats();
assertEmpty(buffer);
}
StatsBuffer getWithWrap() {
StatsBuffer buffer = new StatsBuffer(SIZE, PERCENTILES);
for (int i = SIZE * 2; i > 0; --i) {
buffer.record(i);
}
buffer.computeStats();
return buffer;
}
@Test
public void testMaxWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getMax(), SIZE);
}
@Test
public void testMinWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getMin(), 1);
}
@Test
public void testCountWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getCount(), SIZE);
}
static final long EXPECTED_TOTAL_WRAP = SIZE * (SIZE + 1) / 2;
@Test
public void testTotalWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getTotalTime(), EXPECTED_TOTAL_WRAP);
}
@Test
public void testMeanWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getMean(), (double) EXPECTED_TOTAL_WRAP / SIZE);
}
static final double EXPECTED_VARIANCE_WRAP = 83333.25;
@Test
public void testVarianceWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getVariance(), EXPECTED_VARIANCE_WRAP, 1e-4);
}
@Test
public void testStdDevWrap() {
StatsBuffer buffer = getWithWrap();
assertEquals(buffer.getStdDev(), Math.sqrt(EXPECTED_VARIANCE_WRAP), 1e-4);
}
@Test
public void testPercentiles50Wrap() {
StatsBuffer buffer = getWithWrap();
double[] percentiles = buffer.getPercentileValues();
// testNG does not give good errors if we do assertEquals on the two arrays
assertEquals(percentiles[0], 501.0);
}
@Test
public void testPercentiles95Wrap() {
StatsBuffer buffer = getWithWrap();
double[] percentiles = buffer.getPercentileValues();
assertEquals(percentiles[1], 951.0);
}
@Test
public void testPercentiles99Wrap() {
StatsBuffer buffer = getWithWrap();
double[] percentiles = buffer.getPercentileValues();
assertEquals(percentiles[2], 991.0);
}
@Test
public void testPercentiles995Wrap() {
StatsBuffer buffer = getWithWrap();
double[] percentiles = buffer.getPercentileValues();
assertEquals(percentiles[3], 996.0);
}
// Used to access private count field via reflection so we can quickly simulate
// a count that will cause an integer overflow.
private void setCount(StatsBuffer buffer, int v) throws Exception {
Class<?> cls = buffer.getClass();
Field field = cls.getDeclaredField("pos");
field.setAccessible(true);
field.set(buffer, v);
}
// Before fix this would throw an ArrayIndexOutOfBoundException
@Test
public void testCountOverflow() throws Exception {
StatsBuffer buffer = new StatsBuffer(SIZE, PERCENTILES);
setCount(buffer, Integer.MAX_VALUE);
buffer.record(1);
buffer.record(2);
}
// java.lang.IllegalArgumentException: fromIndex(0) > toIndex(-2147483647)
@Test
public void testComputeStatsWithOverflow() throws Exception {
StatsBuffer buffer = new StatsBuffer(SIZE, PERCENTILES);
setCount(buffer, Integer.MAX_VALUE);
buffer.record(1);
buffer.record(2);
buffer.computeStats();
}
}