| /* |
| * 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.Distribution.Bucket; |
| import io.opencensus.metrics.Point; |
| import io.opencensus.metrics.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)); |
| assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toPoint(TIMESTAMP)) |
| .isEqualTo( |
| Point.create( |
| Value.distributionValue( |
| io.opencensus.metrics.Distribution.create( |
| 0, |
| 0, |
| 0, |
| BUCKET_BOUNDARIES.getBoundaries(), |
| Arrays.asList( |
| Bucket.create(0), |
| Bucket.create(0), |
| Bucket.create(0), |
| Bucket.create(0)))), |
| TIMESTAMP)); |
| assertThat(MutableLastValueDouble.create().toPoint(TIMESTAMP)) |
| .isEqualTo(Point.create(Value.doubleValue(Double.NaN), TIMESTAMP)); |
| assertThat(MutableLastValueLong.create().toPoint(TIMESTAMP)) |
| .isEqualTo(Point.create(Value.longValue(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); |
| } |
| } |