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