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.MutableViewData.ZERO_TIMESTAMP; 21 22 import com.google.common.collect.Iterables; 23 import com.google.common.collect.Lists; 24 import io.opencensus.common.Function; 25 import io.opencensus.common.Functions; 26 import io.opencensus.common.Timestamp; 27 import io.opencensus.metrics.data.AttachmentValue; 28 import io.opencensus.stats.Aggregation; 29 import io.opencensus.stats.AggregationData; 30 import io.opencensus.stats.AggregationData.CountData; 31 import io.opencensus.stats.AggregationData.DistributionData; 32 import io.opencensus.stats.AggregationData.LastValueDataDouble; 33 import io.opencensus.stats.AggregationData.LastValueDataLong; 34 import io.opencensus.stats.AggregationData.MeanData; 35 import io.opencensus.stats.AggregationData.SumDataDouble; 36 import io.opencensus.stats.AggregationData.SumDataLong; 37 import io.opencensus.stats.Measure; 38 import io.opencensus.stats.View; 39 import io.opencensus.stats.ViewData; 40 import io.opencensus.stats.ViewData.AggregationWindowData; 41 import io.opencensus.stats.ViewData.AggregationWindowData.CumulativeData; 42 import io.opencensus.stats.ViewData.AggregationWindowData.IntervalData; 43 import io.opencensus.tags.Tag; 44 import io.opencensus.tags.TagContext; 45 import io.opencensus.tags.TagValue; 46 import java.util.ArrayList; 47 import java.util.Collections; 48 import java.util.Iterator; 49 import java.util.List; 50 import java.util.Map; 51 import java.util.Map.Entry; 52 import javax.annotation.Nullable; 53 54 /** Stats test utilities. */ 55 final class StatsTestUtil { 56 57 private static final Timestamp EMPTY = Timestamp.create(0, 0); 58 StatsTestUtil()59 private StatsTestUtil() {} 60 61 /** 62 * Creates an {@link AggregationData} by adding the given sequence of values, based on the 63 * definition of the given {@link Aggregation}. 64 * 65 * @param aggregation the {@code Aggregation} to apply the values to. 66 * @param values the values to add to the {@code MutableAggregation}s. 67 * @return an {@code AggregationData}. 68 */ createAggregationData( Aggregation aggregation, Measure measure, double... values)69 static AggregationData createAggregationData( 70 Aggregation aggregation, Measure measure, double... values) { 71 MutableAggregation mutableAggregation = 72 RecordUtils.createMutableAggregation(aggregation, measure); 73 for (double value : values) { 74 mutableAggregation.add(value, Collections.<String, AttachmentValue>emptyMap(), EMPTY); 75 } 76 return mutableAggregation.toAggregationData(); 77 } 78 79 /** 80 * Compare the actual and expected AggregationMap within the given tolerance. 81 * 82 * @param expected the expected map. 83 * @param actual the actual mapping from {@code List<TagValue>} to {@code AggregationData}. 84 * @param tolerance the tolerance used for {@code double} comparison. 85 */ assertAggregationMapEquals( Map<? extends List<? extends TagValue>, ? extends AggregationData> actual, Map<? extends List<? extends TagValue>, ? extends AggregationData> expected, double tolerance)86 static void assertAggregationMapEquals( 87 Map<? extends List<? extends TagValue>, ? extends AggregationData> actual, 88 Map<? extends List<? extends TagValue>, ? extends AggregationData> expected, 89 double tolerance) { 90 assertThat(actual.keySet()).containsExactlyElementsIn(expected.keySet()); 91 for (Entry<? extends List<? extends TagValue>, ? extends AggregationData> entry : 92 actual.entrySet()) { 93 assertAggregationDataEquals(expected.get(entry.getKey()), entry.getValue(), tolerance); 94 } 95 } 96 97 /** 98 * Compare the expected and actual {@code AggregationData} within the given tolerance. 99 * 100 * @param expected the expected {@code AggregationData}. 101 * @param actual the actual {@code AggregationData}. 102 * @param tolerance the tolerance used for {@code double} comparison. 103 */ assertAggregationDataEquals( AggregationData expected, final AggregationData actual, final double tolerance)104 static void assertAggregationDataEquals( 105 AggregationData expected, final AggregationData actual, final double tolerance) { 106 expected.match( 107 new Function<SumDataDouble, Void>() { 108 @Override 109 public Void apply(SumDataDouble arg) { 110 assertThat(actual).isInstanceOf(SumDataDouble.class); 111 assertThat(((SumDataDouble) actual).getSum()).isWithin(tolerance).of(arg.getSum()); 112 return null; 113 } 114 }, 115 new Function<SumDataLong, Void>() { 116 @Override 117 public Void apply(SumDataLong arg) { 118 assertThat(actual).isInstanceOf(SumDataLong.class); 119 assertThat(((SumDataLong) actual).getSum()).isEqualTo(arg.getSum()); 120 return null; 121 } 122 }, 123 new Function<CountData, Void>() { 124 @Override 125 public Void apply(CountData arg) { 126 assertThat(actual).isInstanceOf(CountData.class); 127 assertThat(((CountData) actual).getCount()).isEqualTo(arg.getCount()); 128 return null; 129 } 130 }, 131 new Function<DistributionData, Void>() { 132 @Override 133 public Void apply(DistributionData arg) { 134 assertThat(actual).isInstanceOf(DistributionData.class); 135 assertDistributionDataEquals(arg, (DistributionData) actual, tolerance); 136 return null; 137 } 138 }, 139 new Function<LastValueDataDouble, Void>() { 140 @Override 141 public Void apply(LastValueDataDouble arg) { 142 assertThat(actual).isInstanceOf(LastValueDataDouble.class); 143 assertThat(((LastValueDataDouble) actual).getLastValue()) 144 .isWithin(tolerance) 145 .of(arg.getLastValue()); 146 return null; 147 } 148 }, 149 new Function<LastValueDataLong, Void>() { 150 @Override 151 public Void apply(LastValueDataLong arg) { 152 assertThat(actual).isInstanceOf(LastValueDataLong.class); 153 assertThat(((LastValueDataLong) actual).getLastValue()).isEqualTo(arg.getLastValue()); 154 return null; 155 } 156 }, 157 new Function<AggregationData, Void>() { 158 @Override 159 public Void apply(AggregationData arg) { 160 if (arg instanceof MeanData) { 161 assertThat(actual).isInstanceOf(MeanData.class); 162 assertThat(((MeanData) actual).getMean()) 163 .isWithin(tolerance) 164 .of(((MeanData) arg).getMean()); 165 return null; 166 } 167 throw new IllegalArgumentException("Unknown Aggregation."); 168 } 169 }); 170 } 171 172 // Create an empty ViewData with the given View. createEmptyViewData(View view)173 static ViewData createEmptyViewData(View view) { 174 return ViewData.create( 175 view, 176 Collections.<List<TagValue>, AggregationData>emptyMap(), 177 view.getWindow() 178 .match( 179 Functions.<AggregationWindowData>returnConstant( 180 CumulativeData.create(ZERO_TIMESTAMP, ZERO_TIMESTAMP)), 181 Functions.<AggregationWindowData>returnConstant( 182 IntervalData.create(ZERO_TIMESTAMP)), 183 Functions.<AggregationWindowData>throwAssertionError())); 184 } 185 186 // Compare the expected and actual DistributionData within the given tolerance. assertDistributionDataEquals( DistributionData expected, DistributionData actual, double tolerance)187 private static void assertDistributionDataEquals( 188 DistributionData expected, DistributionData actual, double tolerance) { 189 assertThat(actual.getMean()).isWithin(tolerance).of(expected.getMean()); 190 assertThat(actual.getCount()).isEqualTo(expected.getCount()); 191 assertThat(actual.getMean()).isWithin(tolerance).of(expected.getMean()); 192 assertThat(actual.getSumOfSquaredDeviations()) 193 .isWithin(tolerance) 194 .of(expected.getSumOfSquaredDeviations()); 195 196 assertThat(removeTrailingZeros(actual.getBucketCounts())) 197 .isEqualTo(removeTrailingZeros(expected.getBucketCounts())); 198 } 199 200 @Nullable removeTrailingZeros(List<Long> longs)201 private static List<Long> removeTrailingZeros(List<Long> longs) { 202 if (longs == null) { 203 return null; 204 } 205 List<Long> truncated = new ArrayList<Long>(longs); 206 while (!truncated.isEmpty() && Iterables.getLast(truncated) == 0) { 207 truncated.remove(truncated.size() - 1); 208 } 209 return truncated; 210 } 211 212 static final class SimpleTagContext extends TagContext { 213 private final List<Tag> tags; 214 SimpleTagContext(Tag... tags)215 SimpleTagContext(Tag... tags) { 216 this.tags = Collections.unmodifiableList(Lists.newArrayList(tags)); 217 } 218 219 @Override getIterator()220 protected Iterator<Tag> getIterator() { 221 return tags.iterator(); 222 } 223 } 224 } 225