• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017, OpenCensus Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.opencensus.implcore.stats;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static io.opencensus.implcore.stats.StatsTestUtil.assertAggregationDataEquals;
21 
22 import com.google.common.collect.ImmutableList;
23 import io.opencensus.common.Timestamp;
24 import io.opencensus.implcore.stats.MutableAggregation.MutableCount;
25 import io.opencensus.implcore.stats.MutableAggregation.MutableDistribution;
26 import io.opencensus.implcore.stats.MutableAggregation.MutableLastValueDouble;
27 import io.opencensus.implcore.stats.MutableAggregation.MutableLastValueLong;
28 import io.opencensus.implcore.stats.MutableAggregation.MutableMean;
29 import io.opencensus.implcore.stats.MutableAggregation.MutableSumDouble;
30 import io.opencensus.implcore.stats.MutableAggregation.MutableSumLong;
31 import io.opencensus.metrics.export.Distribution;
32 import io.opencensus.metrics.export.Distribution.Bucket;
33 import io.opencensus.metrics.export.Distribution.BucketOptions;
34 import io.opencensus.metrics.export.Point;
35 import io.opencensus.metrics.export.Value;
36 import io.opencensus.stats.AggregationData;
37 import io.opencensus.stats.AggregationData.CountData;
38 import io.opencensus.stats.AggregationData.DistributionData;
39 import io.opencensus.stats.AggregationData.DistributionData.Exemplar;
40 import io.opencensus.stats.AggregationData.LastValueDataDouble;
41 import io.opencensus.stats.AggregationData.LastValueDataLong;
42 import io.opencensus.stats.AggregationData.MeanData;
43 import io.opencensus.stats.AggregationData.SumDataDouble;
44 import io.opencensus.stats.AggregationData.SumDataLong;
45 import io.opencensus.stats.BucketBoundaries;
46 import java.util.Arrays;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.Map;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.rules.ExpectedException;
53 import org.junit.runner.RunWith;
54 import org.junit.runners.JUnit4;
55 
56 /** Unit tests for {@link io.opencensus.implcore.stats.MutableAggregation}. */
57 @RunWith(JUnit4.class)
58 public class MutableAggregationTest {
59 
60   @Rule public ExpectedException thrown = ExpectedException.none();
61 
62   private static final double TOLERANCE = 1e-6;
63   private static final BucketBoundaries BUCKET_BOUNDARIES =
64       BucketBoundaries.create(Arrays.asList(-10.0, 0.0, 10.0));
65   private static final BucketBoundaries BUCKET_BOUNDARIES_EMPTY =
66       BucketBoundaries.create(Collections.<Double>emptyList());
67   private static final Timestamp TIMESTAMP = Timestamp.create(60, 0);
68 
69   @Test
testCreateEmpty()70   public void testCreateEmpty() {
71     assertThat(MutableSumDouble.create().getSum()).isWithin(TOLERANCE).of(0);
72     assertThat(MutableSumLong.create().getSum()).isWithin(TOLERANCE).of(0);
73     assertThat(MutableCount.create().getCount()).isEqualTo(0);
74     assertThat(MutableMean.create().getMean()).isWithin(TOLERANCE).of(0);
75     assertThat(MutableLastValueDouble.create().getLastValue()).isNaN();
76     assertThat(MutableLastValueLong.create().getLastValue()).isNaN();
77 
78     BucketBoundaries bucketBoundaries = BucketBoundaries.create(Arrays.asList(0.1, 2.2, 33.3));
79     MutableDistribution mutableDistribution = MutableDistribution.create(bucketBoundaries);
80     assertThat(mutableDistribution.getMean()).isWithin(TOLERANCE).of(0);
81     assertThat(mutableDistribution.getCount()).isEqualTo(0);
82     assertThat(mutableDistribution.getMin()).isPositiveInfinity();
83     assertThat(mutableDistribution.getMax()).isNegativeInfinity();
84     assertThat(mutableDistribution.getSumOfSquaredDeviations()).isWithin(TOLERANCE).of(0);
85     assertThat(mutableDistribution.getBucketCounts()).isEqualTo(new long[4]);
86     assertThat(mutableDistribution.getExemplars()).isEqualTo(new Exemplar[4]);
87 
88     MutableDistribution mutableDistributionNoHistogram =
89         MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY);
90     assertThat(mutableDistributionNoHistogram.getExemplars()).isNull();
91   }
92 
93   @Test
testNullBucketBoundaries()94   public void testNullBucketBoundaries() {
95     thrown.expect(NullPointerException.class);
96     thrown.expectMessage("bucketBoundaries should not be null.");
97     MutableDistribution.create(null);
98   }
99 
100   @Test
testNoBoundaries()101   public void testNoBoundaries() {
102     List<Double> buckets = Arrays.asList();
103     MutableDistribution noBoundaries = MutableDistribution.create(BucketBoundaries.create(buckets));
104     assertThat(noBoundaries.getBucketCounts().length).isEqualTo(1);
105     assertThat(noBoundaries.getBucketCounts()[0]).isEqualTo(0);
106   }
107 
108   @Test
testAdd()109   public void testAdd() {
110     List<MutableAggregation> aggregations =
111         Arrays.asList(
112             MutableSumDouble.create(),
113             MutableSumLong.create(),
114             MutableCount.create(),
115             MutableMean.create(),
116             MutableDistribution.create(BUCKET_BOUNDARIES),
117             MutableLastValueDouble.create(),
118             MutableLastValueLong.create());
119 
120     List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0);
121 
122     for (double value : values) {
123       for (MutableAggregation aggregation : aggregations) {
124         aggregation.add(value, Collections.<String, String>emptyMap(), TIMESTAMP);
125       }
126     }
127 
128     assertAggregationDataEquals(
129         aggregations.get(0).toAggregationData(),
130         AggregationData.SumDataDouble.create(20.0),
131         TOLERANCE);
132     assertAggregationDataEquals(
133         aggregations.get(1).toAggregationData(), AggregationData.SumDataLong.create(20), TOLERANCE);
134     assertAggregationDataEquals(
135         aggregations.get(2).toAggregationData(), AggregationData.CountData.create(5), TOLERANCE);
136     assertAggregationDataEquals(
137         aggregations.get(3).toAggregationData(),
138         AggregationData.MeanData.create(4.0, 5),
139         TOLERANCE);
140     assertAggregationDataEquals(
141         aggregations.get(4).toAggregationData(),
142         AggregationData.DistributionData.create(
143             4.0, 5, -5.0, 20.0, 372, Arrays.asList(0L, 2L, 2L, 1L)),
144         TOLERANCE);
145     assertAggregationDataEquals(
146         aggregations.get(5).toAggregationData(),
147         AggregationData.LastValueDataDouble.create(5.0),
148         TOLERANCE);
149     assertAggregationDataEquals(
150         aggregations.get(6).toAggregationData(),
151         AggregationData.LastValueDataLong.create(5),
152         TOLERANCE);
153   }
154 
155   @Test
testAdd_DistributionWithExemplarAttachments()156   public void testAdd_DistributionWithExemplarAttachments() {
157     MutableDistribution mutableDistribution = MutableDistribution.create(BUCKET_BOUNDARIES);
158     MutableDistribution mutableDistributionNoHistogram =
159         MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY);
160     List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0);
161     List<Map<String, String>> attachmentsList =
162         ImmutableList.<Map<String, String>>of(
163             Collections.<String, String>singletonMap("k1", "v1"),
164             Collections.<String, String>singletonMap("k2", "v2"),
165             Collections.<String, String>singletonMap("k3", "v3"),
166             Collections.<String, String>singletonMap("k4", "v4"),
167             Collections.<String, String>singletonMap("k5", "v5"));
168     List<Timestamp> timestamps =
169         Arrays.asList(
170             Timestamp.fromMillis(500),
171             Timestamp.fromMillis(1000),
172             Timestamp.fromMillis(2000),
173             Timestamp.fromMillis(3000),
174             Timestamp.fromMillis(4000));
175     for (int i = 0; i < values.size(); i++) {
176       mutableDistribution.add(values.get(i), attachmentsList.get(i), timestamps.get(i));
177       mutableDistributionNoHistogram.add(values.get(i), attachmentsList.get(i), timestamps.get(i));
178     }
179 
180     // Each bucket can only have up to one exemplar. If there are more than one exemplars in a
181     // bucket, only the last one will be kept.
182     List<Exemplar> expected =
183         Arrays.<Exemplar>asList(
184             null,
185             Exemplar.create(values.get(2), timestamps.get(2), attachmentsList.get(2)),
186             Exemplar.create(values.get(4), timestamps.get(4), attachmentsList.get(4)),
187             Exemplar.create(values.get(3), timestamps.get(3), attachmentsList.get(3)));
188     assertThat(mutableDistribution.getExemplars())
189         .asList()
190         .containsExactlyElementsIn(expected)
191         .inOrder();
192     assertThat(mutableDistributionNoHistogram.getExemplars()).isNull();
193   }
194 
195   @Test
testCombine_SumCountMean()196   public void testCombine_SumCountMean() {
197     // combine() for Mutable Sum, Count and Mean will pick up fractional stats
198     List<MutableAggregation> aggregations1 =
199         Arrays.asList(
200             MutableSumDouble.create(),
201             MutableSumLong.create(),
202             MutableCount.create(),
203             MutableMean.create());
204     List<MutableAggregation> aggregations2 =
205         Arrays.asList(
206             MutableSumDouble.create(),
207             MutableSumLong.create(),
208             MutableCount.create(),
209             MutableMean.create());
210 
211     for (double val : Arrays.asList(-1.0, -5.0)) {
212       for (MutableAggregation aggregation : aggregations1) {
213         aggregation.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
214       }
215     }
216     for (double val : Arrays.asList(10.0, 50.0)) {
217       for (MutableAggregation aggregation : aggregations2) {
218         aggregation.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
219       }
220     }
221 
222     List<MutableAggregation> combined =
223         Arrays.asList(
224             MutableSumDouble.create(),
225             MutableSumLong.create(),
226             MutableCount.create(),
227             MutableMean.create());
228     double fraction1 = 1.0;
229     double fraction2 = 0.6;
230     for (int i = 0; i < combined.size(); i++) {
231       combined.get(i).combine(aggregations1.get(i), fraction1);
232       combined.get(i).combine(aggregations2.get(i), fraction2);
233     }
234 
235     assertThat(((MutableSumDouble) combined.get(0)).getSum()).isWithin(TOLERANCE).of(30);
236     assertThat(((MutableSumLong) combined.get(1)).getSum()).isWithin(TOLERANCE).of(30);
237     assertThat(((MutableCount) combined.get(2)).getCount()).isEqualTo(3);
238     assertThat(((MutableMean) combined.get(3)).getMean()).isWithin(TOLERANCE).of(10);
239   }
240 
241   @Test
testCombine_Distribution()242   public void testCombine_Distribution() {
243     // combine() for Mutable Distribution will ignore fractional stats
244     MutableDistribution distribution1 = MutableDistribution.create(BUCKET_BOUNDARIES);
245     MutableDistribution distribution2 = MutableDistribution.create(BUCKET_BOUNDARIES);
246     MutableDistribution distribution3 = MutableDistribution.create(BUCKET_BOUNDARIES);
247 
248     for (double val : Arrays.asList(5.0, -5.0)) {
249       distribution1.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
250     }
251     for (double val : Arrays.asList(10.0, 20.0)) {
252       distribution2.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
253     }
254     for (double val : Arrays.asList(-10.0, 15.0, -15.0, -20.0)) {
255       distribution3.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
256     }
257 
258     MutableDistribution combined = MutableDistribution.create(BUCKET_BOUNDARIES);
259     combined.combine(distribution1, 1.0); // distribution1 will be combined
260     combined.combine(distribution2, 0.6); // distribution2 will be ignored
261     verifyMutableDistribution(combined, 0, 2, -5, 5, 50.0, new long[] {0, 1, 1, 0}, TOLERANCE);
262 
263     combined.combine(distribution2, 1.0); // distribution2 will be combined
264     verifyMutableDistribution(combined, 7.5, 4, -5, 20, 325.0, new long[] {0, 1, 1, 2}, TOLERANCE);
265 
266     combined.combine(distribution3, 1.0); // distribution3 will be combined
267     verifyMutableDistribution(combined, 0, 8, -20, 20, 1500.0, new long[] {2, 2, 1, 3}, TOLERANCE);
268   }
269 
270   @Test
mutableAggregation_ToAggregationData()271   public void mutableAggregation_ToAggregationData() {
272     assertThat(MutableSumDouble.create().toAggregationData()).isEqualTo(SumDataDouble.create(0));
273     assertThat(MutableSumLong.create().toAggregationData()).isEqualTo(SumDataLong.create(0));
274     assertThat(MutableCount.create().toAggregationData()).isEqualTo(CountData.create(0));
275     assertThat(MutableMean.create().toAggregationData()).isEqualTo(MeanData.create(0, 0));
276     assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toAggregationData())
277         .isEqualTo(
278             DistributionData.create(
279                 0,
280                 0,
281                 Double.POSITIVE_INFINITY,
282                 Double.NEGATIVE_INFINITY,
283                 0,
284                 Arrays.asList(0L, 0L, 0L, 0L)));
285     assertThat(MutableLastValueDouble.create().toAggregationData())
286         .isEqualTo(LastValueDataDouble.create(Double.NaN));
287     assertThat(MutableLastValueLong.create().toAggregationData())
288         .isEqualTo(LastValueDataLong.create(0));
289   }
290 
291   @Test
mutableAggregation_ToPoint()292   public void mutableAggregation_ToPoint() {
293     assertThat(MutableSumDouble.create().toPoint(TIMESTAMP))
294         .isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP));
295     assertThat(MutableSumLong.create().toPoint(TIMESTAMP))
296         .isEqualTo(Point.create(Value.longValue(0), TIMESTAMP));
297     assertThat(MutableCount.create().toPoint(TIMESTAMP))
298         .isEqualTo(Point.create(Value.longValue(0), TIMESTAMP));
299     assertThat(MutableMean.create().toPoint(TIMESTAMP))
300         .isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP));
301 
302     thrown.expect(IllegalArgumentException.class);
303     thrown.expectMessage("bucket boundary should be > 0");
304     assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toPoint(TIMESTAMP))
305         .isEqualTo(
306             Point.create(
307                 Value.distributionValue(
308                     Distribution.create(
309                         0,
310                         0,
311                         0,
312                         BucketOptions.explicitOptions(BUCKET_BOUNDARIES.getBoundaries()),
313                         Arrays.asList(
314                             Bucket.create(0),
315                             Bucket.create(0),
316                             Bucket.create(0),
317                             Bucket.create(0)))),
318                 TIMESTAMP));
319   }
320 
verifyMutableDistribution( MutableDistribution mutableDistribution, double mean, long count, double min, double max, double sumOfSquaredDeviations, long[] bucketCounts, double tolerance)321   private static void verifyMutableDistribution(
322       MutableDistribution mutableDistribution,
323       double mean,
324       long count,
325       double min,
326       double max,
327       double sumOfSquaredDeviations,
328       long[] bucketCounts,
329       double tolerance) {
330     assertThat(mutableDistribution.getMean()).isWithin(tolerance).of(mean);
331     assertThat(mutableDistribution.getCount()).isEqualTo(count);
332     assertThat(mutableDistribution.getMin()).isWithin(tolerance).of(min);
333     assertThat(mutableDistribution.getMax()).isWithin(tolerance).of(max);
334     assertThat(mutableDistribution.getSumOfSquaredDeviations())
335         .isWithin(tolerance)
336         .of(sumOfSquaredDeviations);
337     assertThat(mutableDistribution.getBucketCounts()).isEqualTo(bucketCounts);
338   }
339 }
340