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.data.AttachmentValue; 32 import io.opencensus.metrics.data.AttachmentValue.AttachmentValueString; 33 import io.opencensus.metrics.data.Exemplar; 34 import io.opencensus.metrics.export.Distribution; 35 import io.opencensus.metrics.export.Distribution.Bucket; 36 import io.opencensus.metrics.export.Distribution.BucketOptions; 37 import io.opencensus.metrics.export.Point; 38 import io.opencensus.metrics.export.Value; 39 import io.opencensus.stats.AggregationData; 40 import io.opencensus.stats.AggregationData.CountData; 41 import io.opencensus.stats.AggregationData.DistributionData; 42 import io.opencensus.stats.AggregationData.LastValueDataDouble; 43 import io.opencensus.stats.AggregationData.LastValueDataLong; 44 import io.opencensus.stats.AggregationData.MeanData; 45 import io.opencensus.stats.AggregationData.SumDataDouble; 46 import io.opencensus.stats.AggregationData.SumDataLong; 47 import io.opencensus.stats.BucketBoundaries; 48 import java.util.Arrays; 49 import java.util.Collections; 50 import java.util.List; 51 import java.util.Map; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.rules.ExpectedException; 55 import org.junit.runner.RunWith; 56 import org.junit.runners.JUnit4; 57 58 /** Unit tests for {@link io.opencensus.implcore.stats.MutableAggregation}. */ 59 @RunWith(JUnit4.class) 60 public class MutableAggregationTest { 61 62 @Rule public ExpectedException thrown = ExpectedException.none(); 63 64 private static final double TOLERANCE = 1e-6; 65 private static final BucketBoundaries BUCKET_BOUNDARIES = 66 BucketBoundaries.create(Arrays.asList(-10.0, 0.0, 10.0)); 67 private static final BucketBoundaries BUCKET_BOUNDARIES_EMPTY = 68 BucketBoundaries.create(Collections.<Double>emptyList()); 69 private static final Timestamp TIMESTAMP = Timestamp.create(60, 0); 70 private static final AttachmentValue ATTACHMENT_VALUE_1 = AttachmentValueString.create("v1"); 71 private static final AttachmentValue ATTACHMENT_VALUE_2 = AttachmentValueString.create("v2"); 72 private static final AttachmentValue ATTACHMENT_VALUE_3 = AttachmentValueString.create("v3"); 73 private static final AttachmentValue ATTACHMENT_VALUE_4 = AttachmentValueString.create("v4"); 74 private static final AttachmentValue ATTACHMENT_VALUE_5 = AttachmentValueString.create("v5"); 75 76 @Test testCreateEmpty()77 public void testCreateEmpty() { 78 assertThat(MutableSumDouble.create().getSum()).isWithin(TOLERANCE).of(0); 79 assertThat(MutableSumLong.create().getSum()).isWithin(TOLERANCE).of(0); 80 assertThat(MutableCount.create().getCount()).isEqualTo(0); 81 assertThat(MutableMean.create().getMean()).isWithin(TOLERANCE).of(0); 82 assertThat(MutableLastValueDouble.create().getLastValue()).isNaN(); 83 assertThat(MutableLastValueLong.create().getLastValue()).isNaN(); 84 85 BucketBoundaries bucketBoundaries = BucketBoundaries.create(Arrays.asList(0.1, 2.2, 33.3)); 86 MutableDistribution mutableDistribution = MutableDistribution.create(bucketBoundaries); 87 assertThat(mutableDistribution.getMean()).isWithin(TOLERANCE).of(0); 88 assertThat(mutableDistribution.getCount()).isEqualTo(0); 89 assertThat(mutableDistribution.getSumOfSquaredDeviations()).isWithin(TOLERANCE).of(0); 90 assertThat(mutableDistribution.getBucketCounts()).isEqualTo(new long[4]); 91 assertThat(mutableDistribution.getExemplars()).isEqualTo(new Exemplar[4]); 92 93 MutableDistribution mutableDistributionNoHistogram = 94 MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY); 95 assertThat(mutableDistributionNoHistogram.getExemplars()).isNull(); 96 } 97 98 @Test testNullBucketBoundaries()99 public void testNullBucketBoundaries() { 100 thrown.expect(NullPointerException.class); 101 thrown.expectMessage("bucketBoundaries should not be null."); 102 MutableDistribution.create(null); 103 } 104 105 @Test testNoBoundaries()106 public void testNoBoundaries() { 107 MutableDistribution noBoundaries = 108 MutableDistribution.create(BucketBoundaries.create(Collections.<Double>emptyList())); 109 assertThat(noBoundaries.getBucketCounts().length).isEqualTo(1); 110 assertThat(noBoundaries.getBucketCounts()[0]).isEqualTo(0); 111 } 112 113 @Test testAdd()114 public void testAdd() { 115 List<MutableAggregation> aggregations = 116 Arrays.asList( 117 MutableSumDouble.create(), 118 MutableSumLong.create(), 119 MutableCount.create(), 120 MutableMean.create(), 121 MutableDistribution.create(BUCKET_BOUNDARIES), 122 MutableLastValueDouble.create(), 123 MutableLastValueLong.create()); 124 125 List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0); 126 127 for (double value : values) { 128 for (MutableAggregation aggregation : aggregations) { 129 aggregation.add(value, Collections.<String, AttachmentValue>emptyMap(), TIMESTAMP); 130 } 131 } 132 133 assertAggregationDataEquals( 134 aggregations.get(0).toAggregationData(), 135 AggregationData.SumDataDouble.create(20.0), 136 TOLERANCE); 137 assertAggregationDataEquals( 138 aggregations.get(1).toAggregationData(), AggregationData.SumDataLong.create(20), TOLERANCE); 139 assertAggregationDataEquals( 140 aggregations.get(2).toAggregationData(), AggregationData.CountData.create(5), TOLERANCE); 141 assertAggregationDataEquals( 142 aggregations.get(3).toAggregationData(), 143 AggregationData.MeanData.create(4.0, 5), 144 TOLERANCE); 145 assertAggregationDataEquals( 146 aggregations.get(4).toAggregationData(), 147 AggregationData.DistributionData.create(4.0, 5, 372, Arrays.asList(4L, 1L)), 148 TOLERANCE); 149 assertAggregationDataEquals( 150 aggregations.get(5).toAggregationData(), 151 AggregationData.LastValueDataDouble.create(5.0), 152 TOLERANCE); 153 assertAggregationDataEquals( 154 aggregations.get(6).toAggregationData(), 155 AggregationData.LastValueDataLong.create(5), 156 TOLERANCE); 157 } 158 159 @Test testAdd_DistributionWithExemplarAttachments()160 public void testAdd_DistributionWithExemplarAttachments() { 161 MutableDistribution mutableDistribution = MutableDistribution.create(BUCKET_BOUNDARIES); 162 MutableDistribution mutableDistributionNoHistogram = 163 MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY); 164 List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0); 165 List<Map<String, AttachmentValue>> attachmentsList = 166 ImmutableList.<Map<String, AttachmentValue>>of( 167 Collections.<String, AttachmentValue>singletonMap("k1", ATTACHMENT_VALUE_1), 168 Collections.<String, AttachmentValue>singletonMap("k2", ATTACHMENT_VALUE_2), 169 Collections.<String, AttachmentValue>singletonMap("k3", ATTACHMENT_VALUE_3), 170 Collections.<String, AttachmentValue>singletonMap("k4", ATTACHMENT_VALUE_4), 171 Collections.<String, AttachmentValue>singletonMap("k5", ATTACHMENT_VALUE_5)); 172 List<Timestamp> timestamps = 173 Arrays.asList( 174 Timestamp.fromMillis(500), 175 Timestamp.fromMillis(1000), 176 Timestamp.fromMillis(2000), 177 Timestamp.fromMillis(3000), 178 Timestamp.fromMillis(4000)); 179 for (int i = 0; i < values.size(); i++) { 180 mutableDistribution.add(values.get(i), attachmentsList.get(i), timestamps.get(i)); 181 mutableDistributionNoHistogram.add(values.get(i), attachmentsList.get(i), timestamps.get(i)); 182 } 183 184 // Each bucket can only have up to one exemplar. If there are more than one exemplars in a 185 // bucket, only the last one will be kept. 186 List<Exemplar> expected = 187 Arrays.<Exemplar>asList( 188 Exemplar.create(values.get(4), timestamps.get(4), attachmentsList.get(4)), 189 Exemplar.create(values.get(3), timestamps.get(3), attachmentsList.get(3))); 190 assertThat(mutableDistribution.getExemplars()) 191 .asList() 192 .containsExactlyElementsIn(expected) 193 .inOrder(); 194 assertThat(mutableDistributionNoHistogram.getExemplars()).isNull(); 195 } 196 197 @Test testCombine_SumCountMean()198 public void testCombine_SumCountMean() { 199 // combine() for Mutable Sum, Count and Mean will pick up fractional stats 200 List<MutableAggregation> aggregations1 = 201 Arrays.asList( 202 MutableSumDouble.create(), 203 MutableSumLong.create(), 204 MutableCount.create(), 205 MutableMean.create()); 206 List<MutableAggregation> aggregations2 = 207 Arrays.asList( 208 MutableSumDouble.create(), 209 MutableSumLong.create(), 210 MutableCount.create(), 211 MutableMean.create()); 212 213 for (double val : Arrays.asList(-1.0, -5.0)) { 214 for (MutableAggregation aggregation : aggregations1) { 215 aggregation.add(val, Collections.<String, AttachmentValue>emptyMap(), TIMESTAMP); 216 } 217 } 218 for (double val : Arrays.asList(10.0, 50.0)) { 219 for (MutableAggregation aggregation : aggregations2) { 220 aggregation.add(val, Collections.<String, AttachmentValue>emptyMap(), TIMESTAMP); 221 } 222 } 223 224 List<MutableAggregation> combined = 225 Arrays.asList( 226 MutableSumDouble.create(), 227 MutableSumLong.create(), 228 MutableCount.create(), 229 MutableMean.create()); 230 double fraction1 = 1.0; 231 double fraction2 = 0.6; 232 for (int i = 0; i < combined.size(); i++) { 233 combined.get(i).combine(aggregations1.get(i), fraction1); 234 combined.get(i).combine(aggregations2.get(i), fraction2); 235 } 236 237 assertThat(((MutableSumDouble) combined.get(0)).getSum()).isWithin(TOLERANCE).of(30); 238 assertThat(((MutableSumLong) combined.get(1)).getSum()).isWithin(TOLERANCE).of(30); 239 assertThat(((MutableCount) combined.get(2)).getCount()).isEqualTo(3); 240 assertThat(((MutableMean) combined.get(3)).getMean()).isWithin(TOLERANCE).of(10); 241 } 242 243 @Test testCombine_Distribution()244 public void testCombine_Distribution() { 245 // combine() for Mutable Distribution will ignore fractional stats 246 MutableDistribution distribution1 = MutableDistribution.create(BUCKET_BOUNDARIES); 247 MutableDistribution distribution2 = MutableDistribution.create(BUCKET_BOUNDARIES); 248 MutableDistribution distribution3 = MutableDistribution.create(BUCKET_BOUNDARIES); 249 250 for (double val : Arrays.asList(5.0, -5.0)) { 251 distribution1.add(val, Collections.<String, AttachmentValue>emptyMap(), TIMESTAMP); 252 } 253 for (double val : Arrays.asList(10.0, 20.0)) { 254 distribution2.add(val, Collections.<String, AttachmentValue>emptyMap(), TIMESTAMP); 255 } 256 for (double val : Arrays.asList(-10.0, 15.0, -15.0, -20.0)) { 257 distribution3.add(val, Collections.<String, AttachmentValue>emptyMap(), TIMESTAMP); 258 } 259 260 MutableDistribution combined = MutableDistribution.create(BUCKET_BOUNDARIES); 261 combined.combine(distribution1, 1.0); // distribution1 will be combined 262 combined.combine(distribution2, 0.6); // distribution2 will be ignored 263 verifyMutableDistribution(combined, 0, 2, 50.0, new long[] {2, 0}); 264 265 combined.combine(distribution2, 1.0); // distribution2 will be combined 266 verifyMutableDistribution(combined, 7.5, 4, 325.0, new long[] {2, 2}); 267 combined.combine(distribution3, 1.0); // distribution3 will be combined 268 verifyMutableDistribution(combined, 0, 8, 1500.0, new long[] {5, 3}); 269 } 270 271 @Test mutableAggregation_ToAggregationData()272 public void mutableAggregation_ToAggregationData() { 273 assertThat(MutableSumDouble.create().toAggregationData()).isEqualTo(SumDataDouble.create(0)); 274 assertThat(MutableSumLong.create().toAggregationData()).isEqualTo(SumDataLong.create(0)); 275 assertThat(MutableCount.create().toAggregationData()).isEqualTo(CountData.create(0)); 276 assertThat(MutableMean.create().toAggregationData()).isEqualTo(MeanData.create(0, 0)); 277 assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toAggregationData()) 278 .isEqualTo(DistributionData.create(0, 0, 0, Arrays.asList(0L, 0L))); 279 assertThat(MutableLastValueDouble.create().toAggregationData()) 280 .isEqualTo(LastValueDataDouble.create(Double.NaN)); 281 assertThat(MutableLastValueLong.create().toAggregationData()) 282 .isEqualTo(LastValueDataLong.create(0)); 283 } 284 285 @Test mutableAggregation_ToPoint()286 public void mutableAggregation_ToPoint() { 287 assertThat(MutableSumDouble.create().toPoint(TIMESTAMP)) 288 .isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP)); 289 assertThat(MutableSumLong.create().toPoint(TIMESTAMP)) 290 .isEqualTo(Point.create(Value.longValue(0), TIMESTAMP)); 291 assertThat(MutableCount.create().toPoint(TIMESTAMP)) 292 .isEqualTo(Point.create(Value.longValue(0), TIMESTAMP)); 293 assertThat(MutableMean.create().toPoint(TIMESTAMP)) 294 .isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP)); 295 296 assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toPoint(TIMESTAMP)) 297 .isEqualTo( 298 Point.create( 299 Value.distributionValue( 300 Distribution.create( 301 0, 302 0, 303 0, 304 BucketOptions.explicitOptions(BUCKET_BOUNDARIES.getBoundaries()), 305 Arrays.asList(Bucket.create(0), Bucket.create(0)))), 306 TIMESTAMP)); 307 } 308 verifyMutableDistribution( MutableDistribution mutableDistribution, double mean, long count, double sumOfSquaredDeviations, long[] bucketCounts)309 private static void verifyMutableDistribution( 310 MutableDistribution mutableDistribution, 311 double mean, 312 long count, 313 double sumOfSquaredDeviations, 314 long[] bucketCounts) { 315 assertThat(mutableDistribution.getMean()).isWithin(MutableAggregationTest.TOLERANCE).of(mean); 316 assertThat(mutableDistribution.getCount()).isEqualTo(count); 317 assertThat(mutableDistribution.getSumOfSquaredDeviations()) 318 .isWithin(MutableAggregationTest.TOLERANCE) 319 .of(sumOfSquaredDeviations); 320 assertThat(mutableDistribution.getBucketCounts()).isEqualTo(bucketCounts); 321 } 322 } 323