blob: a6139e53f91042ef74f9574eedcb50ae54435922 [file] [log] [blame]
/*
* Copyright 2017, OpenCensus Authors
*
* 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.opencensus.implcore.stats;
import static com.google.common.truth.Truth.assertThat;
import static io.opencensus.implcore.stats.StatsTestUtil.assertAggregationDataEquals;
import com.google.common.collect.ImmutableList;
import io.opencensus.common.Timestamp;
import io.opencensus.implcore.stats.MutableAggregation.MutableCount;
import io.opencensus.implcore.stats.MutableAggregation.MutableDistribution;
import io.opencensus.implcore.stats.MutableAggregation.MutableLastValueDouble;
import io.opencensus.implcore.stats.MutableAggregation.MutableLastValueLong;
import io.opencensus.implcore.stats.MutableAggregation.MutableMean;
import io.opencensus.implcore.stats.MutableAggregation.MutableSumDouble;
import io.opencensus.implcore.stats.MutableAggregation.MutableSumLong;
import io.opencensus.metrics.export.Distribution;
import io.opencensus.metrics.export.Distribution.Bucket;
import io.opencensus.metrics.export.Distribution.BucketOptions;
import io.opencensus.metrics.export.Point;
import io.opencensus.metrics.export.Value;
import io.opencensus.stats.AggregationData;
import io.opencensus.stats.AggregationData.CountData;
import io.opencensus.stats.AggregationData.DistributionData;
import io.opencensus.stats.AggregationData.DistributionData.Exemplar;
import io.opencensus.stats.AggregationData.LastValueDataDouble;
import io.opencensus.stats.AggregationData.LastValueDataLong;
import io.opencensus.stats.AggregationData.MeanData;
import io.opencensus.stats.AggregationData.SumDataDouble;
import io.opencensus.stats.AggregationData.SumDataLong;
import io.opencensus.stats.BucketBoundaries;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link io.opencensus.implcore.stats.MutableAggregation}. */
@RunWith(JUnit4.class)
public class MutableAggregationTest {
@Rule public ExpectedException thrown = ExpectedException.none();
private static final double TOLERANCE = 1e-6;
private static final BucketBoundaries BUCKET_BOUNDARIES =
BucketBoundaries.create(Arrays.asList(-10.0, 0.0, 10.0));
private static final BucketBoundaries BUCKET_BOUNDARIES_EMPTY =
BucketBoundaries.create(Collections.<Double>emptyList());
private static final Timestamp TIMESTAMP = Timestamp.create(60, 0);
@Test
public void testCreateEmpty() {
assertThat(MutableSumDouble.create().getSum()).isWithin(TOLERANCE).of(0);
assertThat(MutableSumLong.create().getSum()).isWithin(TOLERANCE).of(0);
assertThat(MutableCount.create().getCount()).isEqualTo(0);
assertThat(MutableMean.create().getMean()).isWithin(TOLERANCE).of(0);
assertThat(MutableLastValueDouble.create().getLastValue()).isNaN();
assertThat(MutableLastValueLong.create().getLastValue()).isNaN();
BucketBoundaries bucketBoundaries = BucketBoundaries.create(Arrays.asList(0.1, 2.2, 33.3));
MutableDistribution mutableDistribution = MutableDistribution.create(bucketBoundaries);
assertThat(mutableDistribution.getMean()).isWithin(TOLERANCE).of(0);
assertThat(mutableDistribution.getCount()).isEqualTo(0);
assertThat(mutableDistribution.getMin()).isPositiveInfinity();
assertThat(mutableDistribution.getMax()).isNegativeInfinity();
assertThat(mutableDistribution.getSumOfSquaredDeviations()).isWithin(TOLERANCE).of(0);
assertThat(mutableDistribution.getBucketCounts()).isEqualTo(new long[4]);
assertThat(mutableDistribution.getExemplars()).isEqualTo(new Exemplar[4]);
MutableDistribution mutableDistributionNoHistogram =
MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY);
assertThat(mutableDistributionNoHistogram.getExemplars()).isNull();
}
@Test
public void testNullBucketBoundaries() {
thrown.expect(NullPointerException.class);
thrown.expectMessage("bucketBoundaries should not be null.");
MutableDistribution.create(null);
}
@Test
public void testNoBoundaries() {
List<Double> buckets = Arrays.asList();
MutableDistribution noBoundaries = MutableDistribution.create(BucketBoundaries.create(buckets));
assertThat(noBoundaries.getBucketCounts().length).isEqualTo(1);
assertThat(noBoundaries.getBucketCounts()[0]).isEqualTo(0);
}
@Test
public void testAdd() {
List<MutableAggregation> aggregations =
Arrays.asList(
MutableSumDouble.create(),
MutableSumLong.create(),
MutableCount.create(),
MutableMean.create(),
MutableDistribution.create(BUCKET_BOUNDARIES),
MutableLastValueDouble.create(),
MutableLastValueLong.create());
List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0);
for (double value : values) {
for (MutableAggregation aggregation : aggregations) {
aggregation.add(value, Collections.<String, String>emptyMap(), TIMESTAMP);
}
}
assertAggregationDataEquals(
aggregations.get(0).toAggregationData(),
AggregationData.SumDataDouble.create(20.0),
TOLERANCE);
assertAggregationDataEquals(
aggregations.get(1).toAggregationData(), AggregationData.SumDataLong.create(20), TOLERANCE);
assertAggregationDataEquals(
aggregations.get(2).toAggregationData(), AggregationData.CountData.create(5), TOLERANCE);
assertAggregationDataEquals(
aggregations.get(3).toAggregationData(),
AggregationData.MeanData.create(4.0, 5),
TOLERANCE);
assertAggregationDataEquals(
aggregations.get(4).toAggregationData(),
AggregationData.DistributionData.create(
4.0, 5, -5.0, 20.0, 372, Arrays.asList(0L, 2L, 2L, 1L)),
TOLERANCE);
assertAggregationDataEquals(
aggregations.get(5).toAggregationData(),
AggregationData.LastValueDataDouble.create(5.0),
TOLERANCE);
assertAggregationDataEquals(
aggregations.get(6).toAggregationData(),
AggregationData.LastValueDataLong.create(5),
TOLERANCE);
}
@Test
public void testAdd_DistributionWithExemplarAttachments() {
MutableDistribution mutableDistribution = MutableDistribution.create(BUCKET_BOUNDARIES);
MutableDistribution mutableDistributionNoHistogram =
MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY);
List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0);
List<Map<String, String>> attachmentsList =
ImmutableList.<Map<String, String>>of(
Collections.<String, String>singletonMap("k1", "v1"),
Collections.<String, String>singletonMap("k2", "v2"),
Collections.<String, String>singletonMap("k3", "v3"),
Collections.<String, String>singletonMap("k4", "v4"),
Collections.<String, String>singletonMap("k5", "v5"));
List<Timestamp> timestamps =
Arrays.asList(
Timestamp.fromMillis(500),
Timestamp.fromMillis(1000),
Timestamp.fromMillis(2000),
Timestamp.fromMillis(3000),
Timestamp.fromMillis(4000));
for (int i = 0; i < values.size(); i++) {
mutableDistribution.add(values.get(i), attachmentsList.get(i), timestamps.get(i));
mutableDistributionNoHistogram.add(values.get(i), attachmentsList.get(i), timestamps.get(i));
}
// Each bucket can only have up to one exemplar. If there are more than one exemplars in a
// bucket, only the last one will be kept.
List<Exemplar> expected =
Arrays.<Exemplar>asList(
null,
Exemplar.create(values.get(2), timestamps.get(2), attachmentsList.get(2)),
Exemplar.create(values.get(4), timestamps.get(4), attachmentsList.get(4)),
Exemplar.create(values.get(3), timestamps.get(3), attachmentsList.get(3)));
assertThat(mutableDistribution.getExemplars())
.asList()
.containsExactlyElementsIn(expected)
.inOrder();
assertThat(mutableDistributionNoHistogram.getExemplars()).isNull();
}
@Test
public void testCombine_SumCountMean() {
// combine() for Mutable Sum, Count and Mean will pick up fractional stats
List<MutableAggregation> aggregations1 =
Arrays.asList(
MutableSumDouble.create(),
MutableSumLong.create(),
MutableCount.create(),
MutableMean.create());
List<MutableAggregation> aggregations2 =
Arrays.asList(
MutableSumDouble.create(),
MutableSumLong.create(),
MutableCount.create(),
MutableMean.create());
for (double val : Arrays.asList(-1.0, -5.0)) {
for (MutableAggregation aggregation : aggregations1) {
aggregation.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
}
}
for (double val : Arrays.asList(10.0, 50.0)) {
for (MutableAggregation aggregation : aggregations2) {
aggregation.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
}
}
List<MutableAggregation> combined =
Arrays.asList(
MutableSumDouble.create(),
MutableSumLong.create(),
MutableCount.create(),
MutableMean.create());
double fraction1 = 1.0;
double fraction2 = 0.6;
for (int i = 0; i < combined.size(); i++) {
combined.get(i).combine(aggregations1.get(i), fraction1);
combined.get(i).combine(aggregations2.get(i), fraction2);
}
assertThat(((MutableSumDouble) combined.get(0)).getSum()).isWithin(TOLERANCE).of(30);
assertThat(((MutableSumLong) combined.get(1)).getSum()).isWithin(TOLERANCE).of(30);
assertThat(((MutableCount) combined.get(2)).getCount()).isEqualTo(3);
assertThat(((MutableMean) combined.get(3)).getMean()).isWithin(TOLERANCE).of(10);
}
@Test
public void testCombine_Distribution() {
// combine() for Mutable Distribution will ignore fractional stats
MutableDistribution distribution1 = MutableDistribution.create(BUCKET_BOUNDARIES);
MutableDistribution distribution2 = MutableDistribution.create(BUCKET_BOUNDARIES);
MutableDistribution distribution3 = MutableDistribution.create(BUCKET_BOUNDARIES);
for (double val : Arrays.asList(5.0, -5.0)) {
distribution1.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
}
for (double val : Arrays.asList(10.0, 20.0)) {
distribution2.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
}
for (double val : Arrays.asList(-10.0, 15.0, -15.0, -20.0)) {
distribution3.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
}
MutableDistribution combined = MutableDistribution.create(BUCKET_BOUNDARIES);
combined.combine(distribution1, 1.0); // distribution1 will be combined
combined.combine(distribution2, 0.6); // distribution2 will be ignored
verifyMutableDistribution(combined, 0, 2, -5, 5, 50.0, new long[] {0, 1, 1, 0}, TOLERANCE);
combined.combine(distribution2, 1.0); // distribution2 will be combined
verifyMutableDistribution(combined, 7.5, 4, -5, 20, 325.0, new long[] {0, 1, 1, 2}, TOLERANCE);
combined.combine(distribution3, 1.0); // distribution3 will be combined
verifyMutableDistribution(combined, 0, 8, -20, 20, 1500.0, new long[] {2, 2, 1, 3}, TOLERANCE);
}
@Test
public void mutableAggregation_ToAggregationData() {
assertThat(MutableSumDouble.create().toAggregationData()).isEqualTo(SumDataDouble.create(0));
assertThat(MutableSumLong.create().toAggregationData()).isEqualTo(SumDataLong.create(0));
assertThat(MutableCount.create().toAggregationData()).isEqualTo(CountData.create(0));
assertThat(MutableMean.create().toAggregationData()).isEqualTo(MeanData.create(0, 0));
assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toAggregationData())
.isEqualTo(
DistributionData.create(
0,
0,
Double.POSITIVE_INFINITY,
Double.NEGATIVE_INFINITY,
0,
Arrays.asList(0L, 0L, 0L, 0L)));
assertThat(MutableLastValueDouble.create().toAggregationData())
.isEqualTo(LastValueDataDouble.create(Double.NaN));
assertThat(MutableLastValueLong.create().toAggregationData())
.isEqualTo(LastValueDataLong.create(0));
}
@Test
public void mutableAggregation_ToPoint() {
assertThat(MutableSumDouble.create().toPoint(TIMESTAMP))
.isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP));
assertThat(MutableSumLong.create().toPoint(TIMESTAMP))
.isEqualTo(Point.create(Value.longValue(0), TIMESTAMP));
assertThat(MutableCount.create().toPoint(TIMESTAMP))
.isEqualTo(Point.create(Value.longValue(0), TIMESTAMP));
assertThat(MutableMean.create().toPoint(TIMESTAMP))
.isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP));
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("bucket boundary should be > 0");
assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toPoint(TIMESTAMP))
.isEqualTo(
Point.create(
Value.distributionValue(
Distribution.create(
0,
0,
0,
BucketOptions.explicitOptions(BUCKET_BOUNDARIES.getBoundaries()),
Arrays.asList(
Bucket.create(0),
Bucket.create(0),
Bucket.create(0),
Bucket.create(0)))),
TIMESTAMP));
}
private static void verifyMutableDistribution(
MutableDistribution mutableDistribution,
double mean,
long count,
double min,
double max,
double sumOfSquaredDeviations,
long[] bucketCounts,
double tolerance) {
assertThat(mutableDistribution.getMean()).isWithin(tolerance).of(mean);
assertThat(mutableDistribution.getCount()).isEqualTo(count);
assertThat(mutableDistribution.getMin()).isWithin(tolerance).of(min);
assertThat(mutableDistribution.getMax()).isWithin(tolerance).of(max);
assertThat(mutableDistribution.getSumOfSquaredDeviations())
.isWithin(tolerance)
.of(sumOfSquaredDeviations);
assertThat(mutableDistribution.getBucketCounts()).isEqualTo(bucketCounts);
}
}