/*
* Copyright (C) 2015 SoftIndex 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 io.datakernel.jmx;
import org.junit.Test;
import java.util.List;
import java.util.Random;
import static java.lang.Math.sqrt;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
public class ValueStatsTest {
private static final double SMOOTHING_WINDOW = ValueStats.SMOOTHING_WINDOW_1_MINUTE;
private static final int ONE_SECOND_IN_MILLIS = 1000;
private static final Random RANDOM = new Random();
@Test
public void smoothedAverageAtLimitShouldBeSameAsInputInCaseOfConstantData() {
double smoothingWindow = 10.0;
long currentTimestamp = 0;
ValueStats valueStats = ValueStats.create(smoothingWindow);
int inputValue = 5;
int iterations = 1000;
int refreshPeriod = ONE_SECOND_IN_MILLIS;
for (int i = 0; i < iterations; i++) {
valueStats.recordValue(inputValue);
currentTimestamp += refreshPeriod;
valueStats.refresh(currentTimestamp);
}
double acceptableError = 10E-5;
assertEquals(inputValue, valueStats.getSmoothedAverage(), acceptableError);
}
@Test
public void itShouldReturnProperStandardDeviationAtLimit() {
double smoothingWindow = 100.0;
long currentTimestamp = 0;
ValueStats valueStats = ValueStats.create(smoothingWindow);
int iterations = 10000;
int minValue = 0;
int maxValue = 10;
int refreshPeriod = 100;
for (int i = 0; i < iterations; i++) {
int currentValue = uniformRandom(minValue, maxValue);
valueStats.recordValue(currentValue);
currentTimestamp += refreshPeriod;
valueStats.refresh(currentTimestamp);
}
// standard deviation of uniform distribution
double expectedStandardDeviation = sqrt(((maxValue - minValue + 1) * (maxValue - minValue + 1) - 1) / 12.0);
double acceptableError = 0.1;
assertEquals(expectedStandardDeviation, valueStats.getSmoothedStandardDeviation(), acceptableError);
}
@Test
public void itShouldResetStatsAfterResetMethodCall() {
double smoothingWindow = 10.0;
long currentTimestamp = 0;
ValueStats valueStats = ValueStats.create(smoothingWindow);
int inputValue = 5;
int iterations = 1000;
int refreshPeriod = ONE_SECOND_IN_MILLIS;
for (int i = 0; i < iterations; i++) {
valueStats.recordValue(inputValue);
currentTimestamp += refreshPeriod;
valueStats.refresh(currentTimestamp);
}
double avgBeforeReset = valueStats.getSmoothedAverage();
valueStats.resetStats();
double avgAfterReset = valueStats.getSmoothedAverage();
double acceptableError = 10E-5;
assertEquals(inputValue, avgBeforeReset, acceptableError);
assertEquals(0.0, avgAfterReset, acceptableError);
}
@Test
public void itShouldAccumulateProperly() {
double smoothingWindow = 10.0;
long currentTimestamp = 0;
ValueStats valueStats_1 = ValueStats.create(smoothingWindow);
ValueStats valueStats_2 = ValueStats.create(smoothingWindow);
int inputValue_1 = 5;
int inputValue_2 = 10;
int iterations = 1000;
int refreshPeriod = ONE_SECOND_IN_MILLIS;
for (int i = 0; i < iterations; i++) {
valueStats_1.recordValue(inputValue_1);
valueStats_2.recordValue(inputValue_2);
currentTimestamp += refreshPeriod;
valueStats_1.refresh(currentTimestamp);
valueStats_2.refresh(currentTimestamp);
}
ValueStats accumulator = ValueStats.createAccumulator();
accumulator.add(valueStats_1);
accumulator.add(valueStats_2);
double acceptableError = 10E-5;
double expectedAccumulatedSmoothedAvg = (5 + 10) / 2.0;
assertEquals(expectedAccumulatedSmoothedAvg, accumulator.getSmoothedAverage(), acceptableError);
}
@Test
public void itShouldBuildHistogram() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 15, 500});
// first interval
stats.recordValue(2);
stats.recordValue(3);
stats.recordValue(-5);
stats.recordValue(0);
// second interval
stats.recordValue(10);
stats.recordValue(5);
stats.recordValue(14);
// no data for third interval
// fourth interval
stats.recordValue(600);
stats.recordValue(1000);
// first interval
for (int i = 0; i < 10; i++) {
stats.recordValue(1);
}
List<String> expected = asList(
"( -∞, 5) : 14",
"[ 5, 15) : 3",
"[ 15, 500) : 0",
"[500, +∞) : 2"
);
assertEquals(expected, stats.getHistogram());
}
@Test
public void itShouldNotRenderUnusedLeftAndRightHistogramLevels() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 10, 15, 20, 25, 30, 35});
stats.recordValue(12);
stats.recordValue(14);
stats.recordValue(23);
List<String> expected = asList(
"(-∞, 10) : 0",
"[10, 15) : 2",
"[15, 20) : 0",
"[20, 25) : 1",
"[25, +∞) : 0"
);
assertEquals(expected, stats.getHistogram());
}
@Test
public void itShouldBuildHistogramProperlyInCaseOfOnlyOneIntermediateValue() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 15, 500});
stats.recordValue(17);
List<String> expected = asList(
"( -∞, 15) : 0",
"[ 15, 500) : 1",
"[500, +∞) : 0"
);
assertEquals(expected, stats.getHistogram());
}
@Test
public void itShouldBuildHistogramProperlyInCaseOfOnlyOneRightmostValue() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 15, 500});
stats.recordValue(600);
List<String> expected = asList(
"( -∞, 500) : 0",
"[500, +∞) : 1"
);
assertEquals(expected, stats.getHistogram());
}
@Test
public void itShouldBuildHistogramProperlyInCaseOfOnlyOneLeftmostValue() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 15, 500});
stats.recordValue(-10);
List<String> expected = asList(
"(-∞, 5) : 1",
"[ 5, +∞) : 0"
);
assertEquals(expected, stats.getHistogram());
}
@Test
public void itShouldAccumulateHistogram() {
ValueStats stats_1 = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 10, 15});
ValueStats stats_2 = ValueStats.create(SMOOTHING_WINDOW).withHistogram(new int[]{5, 10, 15});
// first interval
stats_1.recordValue(2);
stats_1.recordValue(4);
stats_2.recordValue(1);
// second interval
stats_1.recordValue(8);
// no data for third interval
// fourth interval
stats_2.recordValue(17);
stats_1.refresh(1L);
stats_2.refresh(1L);
ValueStats accumulator = ValueStats.createAccumulator();
accumulator.add(stats_1);
accumulator.add(stats_2);
List<String> expected = asList(
"(-∞, 5) : 3",
"[ 5, 10) : 1",
"[10, 15) : 0",
"[15, +∞) : 1"
);
assertEquals(expected, accumulator.getHistogram());
}
@Test
public void itShouldProperlyBuild_Pow2_Histogram() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(ValueStats.POWERS_OF_TWO);
stats.recordValue(-10);
stats.recordValue(0);
stats.recordValue(0);
stats.recordValue(2);
stats.recordValue(3);
stats.recordValue(3);
stats.recordValue(7);
List<String> expected = asList(
"(-∞, 0) : 1",
"[ 0, 1) : 2",
"[ 1, 2) : 0",
"[ 2, 4) : 3",
"[ 4, 8) : 1",
"[ 8, +∞) : 0"
);
assertEquals(expected, stats.getHistogram());
}
@Test
public void itShouldProperlyBuild_Pow2_Histogram_withLimitValues() {
ValueStats stats = ValueStats.create(SMOOTHING_WINDOW).withHistogram(ValueStats.POWERS_OF_TWO);
stats.recordValue(Integer.MAX_VALUE);
List<String> expected = asList(
"( -∞, 1073741824) : 0",
"[1073741824, +∞) : 1"
);
assertEquals(expected, stats.getHistogram());
}
public static int uniformRandom(int min, int max) {
return min + (Math.abs(RANDOM.nextInt()) % (max - min + 1));
}
}