diff options
author | Yang Song <songy23@users.noreply.github.com> | 2017-11-09 13:59:08 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-09 13:59:08 -0800 |
commit | 5923bdbee2ea12ad9c4fa962b5adf526d9fe839d (patch) | |
tree | 5eac15ce4178a53c1deb212b050c59034a9a6cfc /impl_core/src/test/java/io/opencensus | |
parent | 32cc8dd045a2502dffa792fd87ff9e994d8e7496 (diff) | |
download | opencensus-java-5923bdbee2ea12ad9c4fa962b5adf526d9fe839d.tar.gz |
Move implementation directories (#786)
* Move directories: core_impl to impl_core, core_impl_java to impl, core_impl_android to impl_lite.
Diffstat (limited to 'impl_core/src/test/java/io/opencensus')
21 files changed, 3422 insertions, 0 deletions
diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/CurrentStatsStateTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/CurrentStatsStateTest.java new file mode 100644 index 00000000..2fd1ddc7 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/CurrentStatsStateTest.java @@ -0,0 +1,65 @@ +/* + * 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 io.opencensus.stats.StatsCollectionState; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link CurrentStatsState}. */ +@RunWith(JUnit4.class) +public final class CurrentStatsStateTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + @Test + public void defaultState() { + assertThat(new CurrentStatsState().get()).isEqualTo(StatsCollectionState.ENABLED); + } + + @Test + public void setState() { + CurrentStatsState state = new CurrentStatsState(); + assertThat(state.set(StatsCollectionState.DISABLED)).isTrue(); + assertThat(state.getInternal()).isEqualTo(StatsCollectionState.DISABLED); + assertThat(state.set(StatsCollectionState.ENABLED)).isTrue(); + assertThat(state.getInternal()).isEqualTo(StatsCollectionState.ENABLED); + assertThat(state.set(StatsCollectionState.ENABLED)).isFalse(); + } + + @Test + public void preventNull() { + CurrentStatsState state = new CurrentStatsState(); + thrown.expect(NullPointerException.class); + thrown.expectMessage("state"); + state.set(null); + } + + @Test + public void preventSettingStateAfterReadingState() { + CurrentStatsState state = new CurrentStatsState(); + state.get(); + thrown.expect(IllegalStateException.class); + thrown.expectMessage("State was already read, cannot set state."); + state.set(StatsCollectionState.DISABLED); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/IntervalBucketTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/IntervalBucketTest.java new file mode 100644 index 00000000..10bce262 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/IntervalBucketTest.java @@ -0,0 +1,120 @@ +/* + * 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 io.opencensus.common.Duration; +import io.opencensus.common.Timestamp; +import io.opencensus.implcore.stats.MutableAggregation.MutableMean; +import io.opencensus.stats.Aggregation.Mean; +import io.opencensus.tags.TagValue; +import java.util.Arrays; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link IntervalBucket}. */ +@RunWith(JUnit4.class) +public class IntervalBucketTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private static final double TOLERANCE = 1e-6; + private static final Duration MINUTE = Duration.create(60, 0); + private static final Duration NEGATIVE_TEN_SEC = Duration.create(-10, 0); + private static final Timestamp START = Timestamp.create(60, 0); + private static final Mean MEAN = Mean.create(); + + @Test + public void preventNullStartTime() { + thrown.expect(NullPointerException.class); + new IntervalBucket(null, MINUTE, MEAN); + } + + @Test + public void preventNullDuration() { + thrown.expect(NullPointerException.class); + new IntervalBucket(START, null, MEAN); + } + + @Test + public void preventNegativeDuration() { + thrown.expect(IllegalArgumentException.class); + new IntervalBucket(START, NEGATIVE_TEN_SEC, MEAN); + } + + @Test + public void preventNullAggregationList() { + thrown.expect(NullPointerException.class); + new IntervalBucket(START, MINUTE, null); + } + + @Test + public void testGetTagValueAggregationMap_empty() { + assertThat(new IntervalBucket(START, MINUTE, MEAN).getTagValueAggregationMap()).isEmpty(); + } + + @Test + public void testGetStart() { + assertThat(new IntervalBucket(START, MINUTE, MEAN).getStart()).isEqualTo(START); + } + + @Test + public void testRecord() { + IntervalBucket bucket = new IntervalBucket(START, MINUTE, MEAN); + List<TagValue> tagValues1 = Arrays.<TagValue>asList(TagValue.create("VALUE1")); + List<TagValue> tagValues2 = Arrays.<TagValue>asList(TagValue.create("VALUE2")); + bucket.record(tagValues1, 5.0); + bucket.record(tagValues1, 15.0); + bucket.record(tagValues2, 10.0); + assertThat(bucket.getTagValueAggregationMap().keySet()).containsExactly(tagValues1, tagValues2); + MutableMean mutableMean1 = (MutableMean) bucket.getTagValueAggregationMap().get(tagValues1); + MutableMean mutableMean2 = (MutableMean) bucket.getTagValueAggregationMap().get(tagValues2); + assertThat(mutableMean1.getSum()).isWithin(TOLERANCE).of(20); + assertThat(mutableMean2.getSum()).isWithin(TOLERANCE).of(10); + assertThat(mutableMean1.getCount()).isEqualTo(2); + assertThat(mutableMean2.getCount()).isEqualTo(1); + } + + @Test + public void testGetFraction() { + Timestamp thirtySecondsAfterStart = Timestamp.create(90, 0); + assertThat(new IntervalBucket(START, MINUTE, MEAN).getFraction(thirtySecondsAfterStart)) + .isWithin(TOLERANCE) + .of(0.5); + } + + @Test + public void preventCallingGetFractionOnPastBuckets() { + IntervalBucket bucket = new IntervalBucket(START, MINUTE, MEAN); + Timestamp twoMinutesAfterStart = Timestamp.create(180, 0); + thrown.expect(IllegalArgumentException.class); + bucket.getFraction(twoMinutesAfterStart); + } + + @Test + public void preventCallingGetFractionOnFutureBuckets() { + IntervalBucket bucket = new IntervalBucket(START, MINUTE, MEAN); + Timestamp thirtySecondsBeforeStart = Timestamp.create(30, 0); + thrown.expect(IllegalArgumentException.class); + bucket.getFraction(thirtySecondsBeforeStart); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureMapInternalTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureMapInternalTest.java new file mode 100644 index 00000000..c57baa0b --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureMapInternalTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2016-17, 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 com.google.common.collect.Lists; +import io.opencensus.stats.Measure; +import io.opencensus.stats.Measure.MeasureDouble; +import io.opencensus.stats.Measure.MeasureLong; +import io.opencensus.stats.Measurement; +import io.opencensus.stats.Measurement.MeasurementDouble; +import io.opencensus.stats.Measurement.MeasurementLong; +import java.util.ArrayList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link MeasureMapInternal}. */ +@RunWith(JUnit4.class) +public class MeasureMapInternalTest { + + @Test + public void testPutDouble() { + MeasureMapInternal metrics = MeasureMapInternal.builder().put(M1, 44.4).build(); + assertContains(metrics, MeasurementDouble.create(M1, 44.4)); + } + + @Test + public void testPutLong() { + MeasureMapInternal metrics = MeasureMapInternal.builder().put(M3, 9999L).put(M4, 8888L).build(); + assertContains(metrics, MeasurementLong.create(M3, 9999L), MeasurementLong.create(M4, 8888L)); + } + + @Test + public void testCombination() { + MeasureMapInternal metrics = + MeasureMapInternal.builder() + .put(M1, 44.4) + .put(M2, 66.6) + .put(M3, 9999L) + .put(M4, 8888L) + .build(); + assertContains( + metrics, + MeasurementDouble.create(M1, 44.4), + MeasurementDouble.create(M2, 66.6), + MeasurementLong.create(M3, 9999L), + MeasurementLong.create(M4, 8888L)); + } + + @Test + public void testBuilderEmpty() { + MeasureMapInternal metrics = MeasureMapInternal.builder().build(); + assertContains(metrics); + } + + @Test + public void testBuilder() { + ArrayList<Measurement> expected = new ArrayList<Measurement>(10); + MeasureMapInternal.Builder builder = MeasureMapInternal.builder(); + for (int i = 1; i <= 10; i++) { + expected.add(MeasurementDouble.create(makeSimpleMeasureDouble("m" + i), i * 11.1)); + builder.put(makeSimpleMeasureDouble("m" + i), i * 11.1); + assertContains(builder.build(), expected.toArray(new Measurement[i])); + } + } + + @Test + public void testDuplicateMeasureDoubles() { + assertContains( + MeasureMapInternal.builder().put(M1, 1.0).put(M1, 2.0).build(), + MeasurementDouble.create(M1, 2.0)); + assertContains( + MeasureMapInternal.builder().put(M1, 1.0).put(M1, 2.0).put(M1, 3.0).build(), + MeasurementDouble.create(M1, 3.0)); + assertContains( + MeasureMapInternal.builder().put(M1, 1.0).put(M2, 2.0).put(M1, 3.0).build(), + MeasurementDouble.create(M1, 3.0), + MeasurementDouble.create(M2, 2.0)); + assertContains( + MeasureMapInternal.builder().put(M1, 1.0).put(M1, 2.0).put(M2, 2.0).build(), + MeasurementDouble.create(M1, 2.0), + MeasurementDouble.create(M2, 2.0)); + } + + @Test + public void testDuplicateMeasureLongs() { + assertContains( + MeasureMapInternal.builder().put(M3, 100L).put(M3, 100L).build(), + MeasurementLong.create(M3, 100L)); + assertContains( + MeasureMapInternal.builder().put(M3, 100L).put(M3, 200L).put(M3, 300L).build(), + MeasurementLong.create(M3, 300L)); + assertContains( + MeasureMapInternal.builder().put(M3, 100L).put(M4, 200L).put(M3, 300L).build(), + MeasurementLong.create(M3, 300L), + MeasurementLong.create(M4, 200L)); + assertContains( + MeasureMapInternal.builder().put(M3, 100L).put(M3, 200L).put(M4, 200L).build(), + MeasurementLong.create(M3, 200L), + MeasurementLong.create(M4, 200L)); + } + + @Test + public void testDuplicateMeasures() { + assertContains( + MeasureMapInternal.builder().put(M3, 100L).put(M1, 1.0).put(M3, 300L).build(), + MeasurementLong.create(M3, 300L), + MeasurementDouble.create(M1, 1.0)); + assertContains( + MeasureMapInternal.builder().put(M2, 2.0).put(M3, 100L).put(M2, 3.0).build(), + MeasurementDouble.create(M2, 3.0), + MeasurementLong.create(M3, 100L)); + } + + private static final MeasureDouble M1 = makeSimpleMeasureDouble("m1"); + private static final MeasureDouble M2 = makeSimpleMeasureDouble("m2"); + private static final MeasureLong M3 = makeSimpleMeasureLong("m3"); + private static final MeasureLong M4 = makeSimpleMeasureLong("m4"); + + private static MeasureDouble makeSimpleMeasureDouble(String measure) { + return Measure.MeasureDouble.create(measure, measure + " description", "1"); + } + + private static MeasureLong makeSimpleMeasureLong(String measure) { + return Measure.MeasureLong.create(measure, measure + " description", "1"); + } + + private static void assertContains(MeasureMapInternal metrics, Measurement... measurements) { + assertThat(Lists.newArrayList(metrics.iterator())).containsExactly((Object[]) measurements); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureToViewMapTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureToViewMapTest.java new file mode 100644 index 00000000..0a198b0c --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureToViewMapTest.java @@ -0,0 +1,69 @@ +/* + * 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 io.opencensus.common.Timestamp; +import io.opencensus.stats.Aggregation.Mean; +import io.opencensus.stats.Measure; +import io.opencensus.stats.StatsCollectionState; +import io.opencensus.stats.View; +import io.opencensus.stats.View.AggregationWindow.Cumulative; +import io.opencensus.stats.View.Name; +import io.opencensus.stats.ViewData; +import io.opencensus.stats.ViewData.AggregationWindowData.CumulativeData; +import io.opencensus.tags.TagKey; +import io.opencensus.testing.common.TestClock; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link MeasureToViewMap}. */ +@RunWith(JUnit4.class) +public class MeasureToViewMapTest { + + private static final Measure MEASURE = + Measure.MeasureDouble.create("my measurement", "measurement description", "By"); + + private static final Name VIEW_NAME = View.Name.create("my view"); + + private static final Cumulative CUMULATIVE = Cumulative.create(); + + private static final View VIEW = + View.create( + VIEW_NAME, + "view description", + MEASURE, + Mean.create(), + Arrays.asList(TagKey.create("my key")), + CUMULATIVE); + + @Test + public void testRegisterAndGetView() { + MeasureToViewMap measureToViewMap = new MeasureToViewMap(); + TestClock clock = TestClock.create(Timestamp.create(10, 20)); + measureToViewMap.registerView(VIEW, clock); + clock.setTime(Timestamp.create(30, 40)); + ViewData viewData = measureToViewMap.getView(VIEW_NAME, clock, StatsCollectionState.ENABLED); + assertThat(viewData.getView()).isEqualTo(VIEW); + assertThat(viewData.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(10, 20), Timestamp.create(30, 40))); + assertThat(viewData.getAggregationMap()).isEmpty(); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/MutableAggregationTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/MutableAggregationTest.java new file mode 100644 index 00000000..054a5b44 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MutableAggregationTest.java @@ -0,0 +1,249 @@ +/* + * 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 io.opencensus.common.Function; +import io.opencensus.implcore.stats.MutableAggregation.MutableCount; +import io.opencensus.implcore.stats.MutableAggregation.MutableDistribution; +import io.opencensus.implcore.stats.MutableAggregation.MutableMean; +import io.opencensus.implcore.stats.MutableAggregation.MutableSum; +import io.opencensus.stats.BucketBoundaries; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +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)); + + @Test + public void testCreateEmpty() { + assertThat(MutableSum.create().getSum()).isWithin(TOLERANCE).of(0); + assertThat(MutableCount.create().getCount()).isEqualTo(0); + assertThat(MutableMean.create().getMean()).isWithin(TOLERANCE).of(0); + + 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]); + } + + @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( + MutableSum.create(), + MutableCount.create(), + MutableMean.create(), + MutableDistribution.create(BUCKET_BOUNDARIES)); + + 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); + } + } + + for (MutableAggregation aggregation : aggregations) { + aggregation.match( + new Function<MutableSum, Void>() { + @Override + public Void apply(MutableSum arg) { + assertThat(arg.getSum()).isWithin(TOLERANCE).of(20.0); + return null; + } + }, + new Function<MutableCount, Void>() { + @Override + public Void apply(MutableCount arg) { + assertThat(arg.getCount()).isEqualTo(5); + return null; + } + }, + new Function<MutableMean, Void>() { + @Override + public Void apply(MutableMean arg) { + assertThat(arg.getMean()).isWithin(TOLERANCE).of(4.0); + return null; + } + }, + new Function<MutableDistribution, Void>() { + @Override + public Void apply(MutableDistribution arg) { + assertThat(arg.getBucketCounts()).isEqualTo(new long[] {0, 2, 2, 1}); + return null; + } + }); + } + } + + @Test + public void testMatch() { + List<MutableAggregation> aggregations = + Arrays.asList( + MutableSum.create(), + MutableCount.create(), + MutableMean.create(), + MutableDistribution.create(BUCKET_BOUNDARIES)); + + List<String> actual = new ArrayList<String>(); + for (MutableAggregation aggregation : aggregations) { + actual.add( + aggregation.match( + new Function<MutableSum, String>() { + @Override + public String apply(MutableSum arg) { + return "SUM"; + } + }, + new Function<MutableCount, String>() { + @Override + public String apply(MutableCount arg) { + return "COUNT"; + } + }, + new Function<MutableMean, String>() { + @Override + public String apply(MutableMean arg) { + return "MEAN"; + } + }, + new Function<MutableDistribution, String>() { + @Override + public String apply(MutableDistribution arg) { + return "DISTRIBUTION"; + } + })); + } + + assertThat(actual).isEqualTo(Arrays.asList("SUM", "COUNT", "MEAN", "DISTRIBUTION")); + } + + @Test + public void testCombine_SumCountMean() { + // combine() for Mutable Sum, Count and Mean will pick up fractional stats + List<MutableAggregation> aggregations1 = + Arrays.asList(MutableSum.create(), MutableCount.create(), MutableMean.create()); + List<MutableAggregation> aggregations2 = + Arrays.asList(MutableSum.create(), MutableCount.create(), MutableMean.create()); + + for (double val : Arrays.asList(-1.0, -5.0)) { + for (MutableAggregation aggregation : aggregations1) { + aggregation.add(val); + } + } + for (double val : Arrays.asList(10.0, 50.0)) { + for (MutableAggregation aggregation : aggregations2) { + aggregation.add(val); + } + } + + List<MutableAggregation> combined = + Arrays.asList(MutableSum.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(((MutableSum) combined.get(0)).getSum()).isWithin(TOLERANCE).of(30); + assertThat(((MutableCount) combined.get(1)).getCount()).isEqualTo(3); + assertThat(((MutableMean) combined.get(2)).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); + } + for (double val : Arrays.asList(10.0, 20.0)) { + distribution2.add(val); + } + for (double val : Arrays.asList(-10.0, 15.0, -15.0, -20.0)) { + distribution3.add(val); + } + + 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); + } + + 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); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/MutableViewDataTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/MutableViewDataTest.java new file mode 100644 index 00000000..b4b4c85b --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MutableViewDataTest.java @@ -0,0 +1,147 @@ +/* + * 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.MutableViewData.toMillis; + +import com.google.common.collect.ImmutableMap; +import io.opencensus.common.Duration; +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.MutableMean; +import io.opencensus.implcore.stats.MutableAggregation.MutableSum; +import io.opencensus.stats.Aggregation.Count; +import io.opencensus.stats.Aggregation.Distribution; +import io.opencensus.stats.Aggregation.Mean; +import io.opencensus.stats.Aggregation.Sum; +import io.opencensus.stats.AggregationData; +import io.opencensus.stats.AggregationData.CountData; +import io.opencensus.stats.AggregationData.DistributionData; +import io.opencensus.stats.AggregationData.MeanData; +import io.opencensus.stats.AggregationData.SumDataDouble; +import io.opencensus.stats.AggregationData.SumDataLong; +import io.opencensus.stats.BucketBoundaries; +import io.opencensus.stats.Measure.MeasureDouble; +import io.opencensus.stats.Measure.MeasureLong; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link MutableViewData}. */ +@RunWith(JUnit4.class) +public class MutableViewDataTest { + + private static final double EPSILON = 1e-7; + + private static final TagKey ORIGINATOR = TagKey.create("originator"); + private static final TagKey CALLER = TagKey.create("caller"); + private static final TagKey METHOD = TagKey.create("method"); + private static final TagValue CALLER_V = TagValue.create("some caller"); + private static final TagValue METHOD_V = TagValue.create("some method"); + private static final MeasureDouble MEASURE_DOUBLE = + MeasureDouble.create("measure1", "description", "1"); + private static final MeasureLong MEASURE_LONG = + MeasureLong.create("measure2", "description", "1"); + + @Test + public void testConstants() { + assertThat(MutableViewData.UNKNOWN_TAG_VALUE).isNull(); + assertThat(MutableViewData.ZERO_TIMESTAMP).isEqualTo(Timestamp.create(0, 0)); + } + + @Test + public void testGetTagValues() { + List<TagKey> columns = Arrays.asList(CALLER, METHOD, ORIGINATOR); + Map<TagKey, TagValue> tags = ImmutableMap.of(CALLER, CALLER_V, METHOD, METHOD_V); + + assertThat(MutableViewData.getTagValues(tags, columns)) + .containsExactly(CALLER_V, METHOD_V, MutableViewData.UNKNOWN_TAG_VALUE) + .inOrder(); + } + + @Test + public void createMutableAggregation() { + BucketBoundaries bucketBoundaries = BucketBoundaries.create(Arrays.asList(-1.0, 0.0, 1.0)); + + assertThat(((MutableSum) MutableViewData.createMutableAggregation(Sum.create())).getSum()) + .isWithin(EPSILON) + .of(0.0); + assertThat(((MutableCount) MutableViewData.createMutableAggregation(Count.create())).getCount()) + .isEqualTo(0L); + assertThat(((MutableMean) MutableViewData.createMutableAggregation(Mean.create())).getMean()) + .isWithin(EPSILON) + .of(0D); + + MutableDistribution mutableDistribution = + (MutableDistribution) + MutableViewData.createMutableAggregation(Distribution.create(bucketBoundaries)); + assertThat(mutableDistribution.getMin()).isPositiveInfinity(); + assertThat(mutableDistribution.getMax()).isNegativeInfinity(); + assertThat(mutableDistribution.getSumOfSquaredDeviations()).isWithin(EPSILON).of(0); + assertThat(mutableDistribution.getBucketCounts()).isEqualTo(new long[4]); + } + + @Test + public void createAggregationData() { + BucketBoundaries bucketBoundaries = BucketBoundaries.create(Arrays.asList(-1.0, 0.0, 1.0)); + List<MutableAggregation> mutableAggregations = + Arrays.asList( + MutableCount.create(), + MutableMean.create(), + MutableDistribution.create(bucketBoundaries)); + List<AggregationData> aggregates = new ArrayList<AggregationData>(); + + aggregates.add(MutableViewData.createAggregationData(MutableSum.create(), MEASURE_DOUBLE)); + aggregates.add(MutableViewData.createAggregationData(MutableSum.create(), MEASURE_LONG)); + for (MutableAggregation mutableAggregation : mutableAggregations) { + aggregates.add(MutableViewData.createAggregationData(mutableAggregation, MEASURE_DOUBLE)); + } + + assertThat(aggregates) + .containsExactly( + SumDataDouble.create(0), + SumDataLong.create(0), + CountData.create(0), + MeanData.create(0, 0), + DistributionData.create( + 0, + 0, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + 0, + Arrays.asList(new Long[] {0L, 0L, 0L, 0L}))) + .inOrder(); + } + + @Test + public void testDurationToMillis() { + assertThat(toMillis(Duration.create(0, 0))).isEqualTo(0); + assertThat(toMillis(Duration.create(0, 987000000))).isEqualTo(987); + assertThat(toMillis(Duration.create(3, 456000000))).isEqualTo(3456); + assertThat(toMillis(Duration.create(0, -1000000))).isEqualTo(-1); + assertThat(toMillis(Duration.create(-1, 0))).isEqualTo(-1000); + assertThat(toMillis(Duration.create(-3, -456000000))).isEqualTo(-3456); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/StatsComponentImplBaseTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsComponentImplBaseTest.java new file mode 100644 index 00000000..49131d8a --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsComponentImplBaseTest.java @@ -0,0 +1,73 @@ +/* + * 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 io.opencensus.implcore.internal.SimpleEventQueue; +import io.opencensus.stats.StatsCollectionState; +import io.opencensus.stats.StatsComponent; +import io.opencensus.testing.common.TestClock; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link StatsComponentImplBase}. */ +@RunWith(JUnit4.class) +public final class StatsComponentImplBaseTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private final StatsComponent statsComponent = + new StatsComponentImplBase(new SimpleEventQueue(), TestClock.create()); + + @Test + public void defaultState() { + assertThat(statsComponent.getState()).isEqualTo(StatsCollectionState.ENABLED); + } + + @Test + public void setState_Disabled() { + statsComponent.setState(StatsCollectionState.DISABLED); + assertThat(statsComponent.getState()).isEqualTo(StatsCollectionState.DISABLED); + } + + @Test + public void setState_Enabled() { + statsComponent.setState(StatsCollectionState.DISABLED); + statsComponent.setState(StatsCollectionState.ENABLED); + assertThat(statsComponent.getState()).isEqualTo(StatsCollectionState.ENABLED); + } + + @Test + public void setState_DisallowsNull() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("newState"); + statsComponent.setState(null); + } + + @Test + public void preventSettingStateAfterGettingState() { + statsComponent.setState(StatsCollectionState.DISABLED); + statsComponent.getState(); + thrown.expect(IllegalStateException.class); + thrown.expectMessage("State was already read, cannot set state."); + statsComponent.setState(StatsCollectionState.ENABLED); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/StatsRecorderImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsRecorderImplTest.java new file mode 100644 index 00000000..b0533d0d --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsRecorderImplTest.java @@ -0,0 +1,207 @@ +/* + * 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.MutableViewData.ZERO_TIMESTAMP; +import static io.opencensus.implcore.stats.StatsTestUtil.createEmptyViewData; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import io.grpc.Context; +import io.opencensus.implcore.internal.SimpleEventQueue; +import io.opencensus.stats.Aggregation.Sum; +import io.opencensus.stats.Measure.MeasureDouble; +import io.opencensus.stats.MeasureMap; +import io.opencensus.stats.StatsCollectionState; +import io.opencensus.stats.StatsComponent; +import io.opencensus.stats.StatsRecorder; +import io.opencensus.stats.View; +import io.opencensus.stats.View.AggregationWindow.Cumulative; +import io.opencensus.stats.ViewData; +import io.opencensus.stats.ViewData.AggregationWindowData.CumulativeData; +import io.opencensus.stats.ViewManager; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.unsafe.ContextUtils; +import io.opencensus.testing.common.TestClock; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link StatsRecorderImpl}. */ +@RunWith(JUnit4.class) +public final class StatsRecorderImplTest { + private static final TagKey KEY = TagKey.create("KEY"); + private static final TagValue VALUE = TagValue.create("VALUE"); + private static final TagValue VALUE_2 = TagValue.create("VALUE_2"); + private static final MeasureDouble MEASURE_DOUBLE = + MeasureDouble.create("my measurement", "description", "us"); + private static final View.Name VIEW_NAME = View.Name.create("my view"); + + private final StatsComponent statsComponent = + new StatsComponentImplBase(new SimpleEventQueue(), TestClock.create()); + + private final ViewManager viewManager = statsComponent.getViewManager(); + private final StatsRecorder statsRecorder = statsComponent.getStatsRecorder(); + + @Test + public void record_CurrentContextNotSet() { + View view = + View.create( + VIEW_NAME, + "description", + MEASURE_DOUBLE, + Sum.create(), + Arrays.asList(KEY), + Cumulative.create()); + viewManager.registerView(view); + statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, 1.0).record(); + ViewData viewData = viewManager.getView(VIEW_NAME); + + // record() should have used the default TagContext, so the tag value should be null. + assertThat(viewData.getAggregationMap().keySet()) + .containsExactly(Arrays.asList((TagValue) null)); + } + + @Test + public void record_CurrentContextSet() { + View view = + View.create( + VIEW_NAME, + "description", + MEASURE_DOUBLE, + Sum.create(), + Arrays.asList(KEY), + Cumulative.create()); + viewManager.registerView(view); + Context orig = + Context.current() + .withValue(ContextUtils.TAG_CONTEXT_KEY, new SimpleTagContext(Tag.create(KEY, VALUE))) + .attach(); + try { + statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, 1.0).record(); + } finally { + Context.current().detach(orig); + } + ViewData viewData = viewManager.getView(VIEW_NAME); + + // record() should have used the given TagContext. + assertThat(viewData.getAggregationMap().keySet()).containsExactly(Arrays.asList(VALUE)); + } + + @Test + public void recordTwice() { + View view = + View.create( + VIEW_NAME, + "description", + MEASURE_DOUBLE, + Sum.create(), + Arrays.asList(KEY), + Cumulative.create()); + viewManager.registerView(view); + MeasureMap statsRecord = statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, 1.0); + statsRecord.record(new SimpleTagContext(Tag.create(KEY, VALUE))); + statsRecord.record(new SimpleTagContext(Tag.create(KEY, VALUE_2))); + ViewData viewData = viewManager.getView(VIEW_NAME); + + // There should be two entries. + StatsTestUtil.assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(Sum.create(), MEASURE_DOUBLE, 1.0), + Arrays.asList(VALUE_2), + StatsTestUtil.createAggregationData(Sum.create(), MEASURE_DOUBLE, 1.0)), + 1e-6); + } + + @Test + public void record_StatsDisabled() { + View view = + View.create( + VIEW_NAME, + "description", + MEASURE_DOUBLE, + Sum.create(), + Arrays.asList(KEY), + Cumulative.create()); + + viewManager.registerView(view); + statsComponent.setState(StatsCollectionState.DISABLED); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.0) + .record(new SimpleTagContext(Tag.create(KEY, VALUE))); + assertThat(viewManager.getView(VIEW_NAME)).isEqualTo(createEmptyViewData(view)); + } + + @Test + public void record_StatsReenabled() { + View view = + View.create( + VIEW_NAME, + "description", + MEASURE_DOUBLE, + Sum.create(), + Arrays.asList(KEY), + Cumulative.create()); + viewManager.registerView(view); + + statsComponent.setState(StatsCollectionState.DISABLED); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.0) + .record(new SimpleTagContext(Tag.create(KEY, VALUE))); + assertThat(viewManager.getView(VIEW_NAME)).isEqualTo(createEmptyViewData(view)); + + statsComponent.setState(StatsCollectionState.ENABLED); + assertThat(viewManager.getView(VIEW_NAME).getAggregationMap()).isEmpty(); + assertThat(viewManager.getView(VIEW_NAME).getWindowData()) + .isNotEqualTo(CumulativeData.create(ZERO_TIMESTAMP, ZERO_TIMESTAMP)); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 4.0) + .record(new SimpleTagContext(Tag.create(KEY, VALUE))); + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(Sum.create(), MEASURE_DOUBLE, 4.0)), + 1e-6); + } + + private static final class SimpleTagContext extends TagContext { + private final List<Tag> tags; + + SimpleTagContext(Tag... tags) { + this.tags = Collections.unmodifiableList(Lists.newArrayList(tags)); + } + + @Override + protected Iterator<Tag> getIterator() { + return tags.iterator(); + } + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/StatsTestUtil.java b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsTestUtil.java new file mode 100644 index 00000000..3a714af4 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsTestUtil.java @@ -0,0 +1,183 @@ +/* + * 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.MutableViewData.ZERO_TIMESTAMP; + +import com.google.common.collect.Iterables; +import io.opencensus.common.Function; +import io.opencensus.common.Functions; +import io.opencensus.stats.Aggregation; +import io.opencensus.stats.AggregationData; +import io.opencensus.stats.AggregationData.CountData; +import io.opencensus.stats.AggregationData.DistributionData; +import io.opencensus.stats.AggregationData.MeanData; +import io.opencensus.stats.AggregationData.SumDataDouble; +import io.opencensus.stats.AggregationData.SumDataLong; +import io.opencensus.stats.Measure; +import io.opencensus.stats.View; +import io.opencensus.stats.ViewData; +import io.opencensus.stats.ViewData.AggregationWindowData; +import io.opencensus.stats.ViewData.AggregationWindowData.CumulativeData; +import io.opencensus.stats.ViewData.AggregationWindowData.IntervalData; +import io.opencensus.tags.TagValue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** Stats test utilities. */ +final class StatsTestUtil { + + private StatsTestUtil() {} + + /** + * Creates an {@link AggregationData} by adding the given sequence of values, based on the + * definition of the given {@link Aggregation}. + * + * @param aggregation the {@code Aggregation} to apply the values to. + * @param values the values to add to the {@code MutableAggregation}s. + * @return an {@code AggregationData}. + */ + static AggregationData createAggregationData( + Aggregation aggregation, Measure measure, double... values) { + MutableAggregation mutableAggregation = MutableViewData.createMutableAggregation(aggregation); + for (double value : values) { + mutableAggregation.add(value); + } + return MutableViewData.createAggregationData(mutableAggregation, measure); + } + + /** + * Compare the actual and expected AggregationMap within the given tolerance. + * + * @param expected the expected map. + * @param actual the actual mapping from {@code List<TagValue>} to {@code AggregationData}. + * @param tolerance the tolerance used for {@code double} comparison. + */ + static void assertAggregationMapEquals( + Map<? extends List<? extends TagValue>, ? extends AggregationData> actual, + Map<? extends List<? extends TagValue>, ? extends AggregationData> expected, + double tolerance) { + assertThat(actual.keySet()).containsExactlyElementsIn(expected.keySet()); + for (List<? extends TagValue> tagValues : actual.keySet()) { + assertAggregationDataEquals(expected.get(tagValues), actual.get(tagValues), tolerance); + } + } + + /** + * Compare the expected and actual {@code AggregationData} within the given tolerance. + * + * @param expected the expected {@code AggregationData}. + * @param actual the actual {@code AggregationData}. + * @param tolerance the tolerance used for {@code double} comparison. + */ + static void assertAggregationDataEquals( + AggregationData expected, final AggregationData actual, final double tolerance) { + expected.match( + new Function<SumDataDouble, Void>() { + @Override + public Void apply(SumDataDouble arg) { + assertThat(actual).isInstanceOf(SumDataDouble.class); + assertThat(((SumDataDouble) actual).getSum()).isWithin(tolerance).of(arg.getSum()); + return null; + } + }, + new Function<SumDataLong, Void>() { + @Override + public Void apply(SumDataLong arg) { + assertThat(actual).isInstanceOf(SumDataLong.class); + assertThat(((SumDataLong) actual).getSum()).isEqualTo(arg.getSum()); + return null; + } + }, + new Function<CountData, Void>() { + @Override + public Void apply(CountData arg) { + assertThat(actual).isInstanceOf(CountData.class); + assertThat(((CountData) actual).getCount()).isEqualTo(arg.getCount()); + return null; + } + }, + new Function<MeanData, Void>() { + @Override + public Void apply(MeanData arg) { + assertThat(actual).isInstanceOf(MeanData.class); + assertThat(((MeanData) actual).getMean()).isWithin(tolerance).of(arg.getMean()); + return null; + } + }, + new Function<DistributionData, Void>() { + @Override + public Void apply(DistributionData arg) { + assertThat(actual).isInstanceOf(DistributionData.class); + assertDistributionDataEquals(arg, (DistributionData) actual, tolerance); + return null; + } + }, + Functions.<Void>throwIllegalArgumentException()); + } + + // Create an empty ViewData with the given View. + static ViewData createEmptyViewData(View view) { + return ViewData.create( + view, + Collections.<List<TagValue>, AggregationData>emptyMap(), + view.getWindow() + .match( + Functions.<AggregationWindowData>returnConstant( + CumulativeData.create(ZERO_TIMESTAMP, ZERO_TIMESTAMP)), + Functions.<AggregationWindowData>returnConstant( + IntervalData.create(ZERO_TIMESTAMP)), + Functions.<AggregationWindowData>throwAssertionError())); + } + + // Compare the expected and actual DistributionData within the given tolerance. + private static void assertDistributionDataEquals( + DistributionData expected, DistributionData actual, double tolerance) { + assertThat(actual.getMean()).isWithin(tolerance).of(expected.getMean()); + assertThat(actual.getCount()).isEqualTo(expected.getCount()); + assertThat(actual.getMean()).isWithin(tolerance).of(expected.getMean()); + assertThat(actual.getSumOfSquaredDeviations()) + .isWithin(tolerance) + .of(expected.getSumOfSquaredDeviations()); + + if (expected.getMax() == Double.NEGATIVE_INFINITY + && expected.getMin() == Double.POSITIVE_INFINITY) { + assertThat(actual.getMax()).isNegativeInfinity(); + assertThat(actual.getMin()).isPositiveInfinity(); + } else { + assertThat(actual.getMax()).isWithin(tolerance).of(expected.getMax()); + assertThat(actual.getMin()).isWithin(tolerance).of(expected.getMin()); + } + + assertThat(removeTrailingZeros((actual).getBucketCounts())) + .isEqualTo(removeTrailingZeros(expected.getBucketCounts())); + } + + private static List<Long> removeTrailingZeros(List<Long> longs) { + if (longs == null) { + return null; + } + List<Long> truncated = new ArrayList<Long>(longs); + while (!truncated.isEmpty() && Iterables.getLast(truncated) == 0) { + truncated.remove(truncated.size() - 1); + } + return truncated; + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java new file mode 100644 index 00000000..5eaa34e5 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java @@ -0,0 +1,924 @@ +/* + * 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.assertAggregationMapEquals; +import static io.opencensus.implcore.stats.StatsTestUtil.createAggregationData; +import static io.opencensus.implcore.stats.StatsTestUtil.createEmptyViewData; + +import com.google.common.collect.ImmutableMap; +import io.opencensus.common.Duration; +import io.opencensus.common.Timestamp; +import io.opencensus.implcore.internal.SimpleEventQueue; +import io.opencensus.implcore.tags.TagsComponentImplBase; +import io.opencensus.stats.Aggregation; +import io.opencensus.stats.Aggregation.Distribution; +import io.opencensus.stats.Aggregation.Mean; +import io.opencensus.stats.Aggregation.Sum; +import io.opencensus.stats.AggregationData; +import io.opencensus.stats.AggregationData.MeanData; +import io.opencensus.stats.AggregationData.SumDataDouble; +import io.opencensus.stats.AggregationData.SumDataLong; +import io.opencensus.stats.BucketBoundaries; +import io.opencensus.stats.Measure; +import io.opencensus.stats.Measure.MeasureDouble; +import io.opencensus.stats.Measure.MeasureLong; +import io.opencensus.stats.MeasureMap; +import io.opencensus.stats.StatsCollectionState; +import io.opencensus.stats.View; +import io.opencensus.stats.View.AggregationWindow.Cumulative; +import io.opencensus.stats.View.AggregationWindow.Interval; +import io.opencensus.stats.View.Name; +import io.opencensus.stats.ViewData; +import io.opencensus.stats.ViewData.AggregationWindowData; +import io.opencensus.stats.ViewData.AggregationWindowData.CumulativeData; +import io.opencensus.stats.ViewData.AggregationWindowData.IntervalData; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opencensus.tags.TagsComponent; +import io.opencensus.testing.common.TestClock; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link ViewManagerImpl}. */ +@RunWith(JUnit4.class) +public class ViewManagerImplTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private static final TagKey KEY = TagKey.create("KEY"); + + private static final TagValue VALUE = TagValue.create("VALUE"); + private static final TagValue VALUE_2 = TagValue.create("VALUE_2"); + + private static final String MEASURE_NAME = "my measurement"; + + private static final String MEASURE_NAME_2 = "my measurement 2"; + + private static final String MEASURE_UNIT = "us"; + + private static final String MEASURE_DESCRIPTION = "measure description"; + + private static final MeasureDouble MEASURE_DOUBLE = + MeasureDouble.create(MEASURE_NAME, MEASURE_DESCRIPTION, MEASURE_UNIT); + + private static final MeasureLong MEASURE_LONG = + MeasureLong.create(MEASURE_NAME_2, MEASURE_DESCRIPTION, MEASURE_UNIT); + + private static final Name VIEW_NAME = Name.create("my view"); + private static final Name VIEW_NAME_2 = Name.create("my view 2"); + + private static final String VIEW_DESCRIPTION = "view description"; + + private static final Cumulative CUMULATIVE = Cumulative.create(); + + private static final double EPSILON = 1e-7; + private static final int MILLIS_PER_SECOND = 1000; + private static final Duration TEN_SECONDS = Duration.create(10, 0); + + private static final BucketBoundaries BUCKET_BOUNDARIES = + BucketBoundaries.create( + Arrays.asList( + 0.0, 0.2, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 7.0, 10.0, 15.0, 20.0, 30.0, 40.0, 50.0)); + + private static final Sum SUM = Sum.create(); + private static final Mean MEAN = Mean.create(); + private static final Distribution DISTRIBUTION = Distribution.create(BUCKET_BOUNDARIES); + + private final TestClock clock = TestClock.create(); + + private final StatsComponentImplBase statsComponent = + new StatsComponentImplBase(new SimpleEventQueue(), clock); + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + + private final Tagger tagger = tagsComponent.getTagger(); + private final ViewManagerImpl viewManager = statsComponent.getViewManager(); + private final StatsRecorderImpl statsRecorder = statsComponent.getStatsRecorder(); + + private static View createCumulativeView() { + return createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY)); + } + + private static View createCumulativeView( + View.Name name, Measure measure, Aggregation aggregation, List<TagKey> keys) { + return View.create(name, VIEW_DESCRIPTION, measure, aggregation, keys, CUMULATIVE); + } + + @Test + public void testRegisterAndGetCumulativeView() { + View view = createCumulativeView(); + viewManager.registerView(view); + assertThat(viewManager.getView(VIEW_NAME).getView()).isEqualTo(view); + assertThat(viewManager.getView(VIEW_NAME).getAggregationMap()).isEmpty(); + assertThat(viewManager.getView(VIEW_NAME).getWindowData()).isInstanceOf(CumulativeData.class); + } + + @Test + public void testRegisterAndGetIntervalView() { + View intervalView = + View.create( + VIEW_NAME, + VIEW_DESCRIPTION, + MEASURE_DOUBLE, + DISTRIBUTION, + Arrays.asList(KEY), + Interval.create(Duration.create(60, 0))); + viewManager.registerView(intervalView); + assertThat(viewManager.getView(VIEW_NAME).getView()).isEqualTo(intervalView); + assertThat(viewManager.getView(VIEW_NAME).getAggregationMap()).isEmpty(); + assertThat(viewManager.getView(VIEW_NAME).getWindowData()).isInstanceOf(IntervalData.class); + } + + @Test + public void allowRegisteringSameViewTwice() { + View view = createCumulativeView(); + viewManager.registerView(view); + viewManager.registerView(view); + assertThat(viewManager.getView(VIEW_NAME).getView()).isEqualTo(view); + } + + @Test + public void preventRegisteringDifferentViewWithSameName() { + View view1 = + View.create( + VIEW_NAME, + "View description.", + MEASURE_DOUBLE, + DISTRIBUTION, + Arrays.asList(KEY), + CUMULATIVE); + View view2 = + View.create( + VIEW_NAME, + "This is a different description.", + MEASURE_DOUBLE, + DISTRIBUTION, + Arrays.asList(KEY), + CUMULATIVE); + testFailedToRegisterView( + view1, view2, "A different view with the same name is already registered"); + } + + @Test + public void preventRegisteringDifferentMeasureWithSameName() { + MeasureDouble measure1 = MeasureDouble.create("measure", "description", "1"); + MeasureLong measure2 = MeasureLong.create("measure", "description", "1"); + View view1 = + View.create( + VIEW_NAME, VIEW_DESCRIPTION, measure1, DISTRIBUTION, Arrays.asList(KEY), CUMULATIVE); + View view2 = + View.create( + VIEW_NAME_2, VIEW_DESCRIPTION, measure2, DISTRIBUTION, Arrays.asList(KEY), CUMULATIVE); + testFailedToRegisterView( + view1, view2, "A different measure with the same name is already registered"); + } + + private void testFailedToRegisterView(View view1, View view2, String message) { + viewManager.registerView(view1); + try { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(message); + viewManager.registerView(view2); + } finally { + assertThat(viewManager.getView(VIEW_NAME).getView()).isEqualTo(view1); + } + } + + @Test + public void returnNullWhenGettingNonexistentViewData() { + assertThat(viewManager.getView(VIEW_NAME)).isNull(); + } + + @Test + public void testRecordDouble_distribution_cumulative() { + testRecordCumulative(MEASURE_DOUBLE, DISTRIBUTION, 10.0, 20.0, 30.0, 40.0); + } + + @Test + public void testRecordLong_distribution_cumulative() { + testRecordCumulative(MEASURE_LONG, DISTRIBUTION, 1000, 2000, 3000, 4000); + } + + @Test + public void testRecordDouble_sum_cumulative() { + testRecordCumulative(MEASURE_DOUBLE, SUM, 11.1, 22.2, 33.3, 44.4); + } + + @Test + public void testRecordLong_sum_cumulative() { + testRecordCumulative(MEASURE_LONG, SUM, 1000, 2000, 3000, 4000); + } + + private void testRecordCumulative(Measure measure, Aggregation aggregation, double... values) { + View view = createCumulativeView(VIEW_NAME, measure, aggregation, Arrays.asList(KEY)); + clock.setTime(Timestamp.create(1, 2)); + viewManager.registerView(view); + TagContext tags = tagger.emptyBuilder().put(KEY, VALUE).build(); + for (double val : values) { + putToMeasureMap(statsRecorder.newMeasureMap(), measure, val).record(tags); + } + clock.setTime(Timestamp.create(3, 4)); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertThat(viewData.getView()).isEqualTo(view); + assertThat(viewData.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(1, 2), Timestamp.create(3, 4))); + StatsTestUtil.assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(aggregation, measure, values)), + EPSILON); + } + + @Test + public void testRecordDouble_mean_interval() { + testRecordInterval( + MEASURE_DOUBLE, + MEAN, + new double[] {20.0, -1.0, 1.0, -5.0, 5.0}, + 9.0, + 30.0, + MeanData.create((19 * 0.6 + 1) / 4, 4), + MeanData.create(0.2 * 5 + 9, 1), + MeanData.create(30.0, 1)); + } + + @Test + public void testRecordLong_mean_interval() { + testRecordInterval( + MEASURE_LONG, + MEAN, + new double[] {1000, 2000, 3000, 4000, 5000}, + -5000, + 30, + MeanData.create((3000 * 0.6 + 12000) / 4, 4), + MeanData.create(-4000, 1), + MeanData.create(30, 1)); + } + + @Test + public void testRecordDouble_sum_interval() { + testRecordInterval( + MEASURE_DOUBLE, + SUM, + new double[] {20.0, -1.0, 1.0, -5.0, 5.0}, + 9.0, + 30.0, + SumDataDouble.create(19 * 0.6 + 1), + SumDataDouble.create(0.2 * 5 + 9), + SumDataDouble.create(30.0)); + } + + @Test + public void testRecordLong_sum_interval() { + testRecordInterval( + MEASURE_LONG, + SUM, + new double[] {1000, 2000, 3000, 4000, 5000}, + -5000, + 30, + SumDataLong.create(Math.round(3000 * 0.6 + 12000)), + SumDataLong.create(-4000), + SumDataLong.create(30)); + } + + private void testRecordInterval( + Measure measure, + Aggregation aggregation, + double[] initialValues, /* There are 5 initial values recorded before we call getView(). */ + double value6, + double value7, + AggregationData expectedValues1, + AggregationData expectedValues2, + AggregationData expectedValues3) { + // The interval is 10 seconds, i.e. values should expire after 10 seconds. + // Each bucket has a duration of 2.5 seconds. + View view = + View.create( + VIEW_NAME, + VIEW_DESCRIPTION, + measure, + aggregation, + Arrays.asList(KEY), + Interval.create(TEN_SECONDS)); + long startTimeMillis = 30 * MILLIS_PER_SECOND; // start at 30s + clock.setTime(Timestamp.fromMillis(startTimeMillis)); + viewManager.registerView(view); + + TagContext tags = tagger.emptyBuilder().put(KEY, VALUE).build(); + + for (int i = 1; i <= 5; i++) { + /* + * Add each value in sequence, at 31s, 32s, 33s, etc. + * 1st and 2nd values should fall into the first bucket [30.0, 32.5), + * 3rd and 4th values should fall into the second bucket [32.5, 35.0), + * 5th value should fall into the third bucket [35.0, 37.5). + */ + clock.setTime(Timestamp.fromMillis(startTimeMillis + i * MILLIS_PER_SECOND)); + putToMeasureMap(statsRecorder.newMeasureMap(), measure, initialValues[i - 1]).record(tags); + } + + clock.setTime(Timestamp.fromMillis(startTimeMillis + 8 * MILLIS_PER_SECOND)); + // 38s, no values should have expired + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(aggregation, measure, initialValues)), + EPSILON); + + clock.setTime(Timestamp.fromMillis(startTimeMillis + 11 * MILLIS_PER_SECOND)); + // 41s, 40% of the values in the first bucket should have expired (1 / 2.5 = 0.4). + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of(Arrays.asList(VALUE), expectedValues1), + EPSILON); + + clock.setTime(Timestamp.fromMillis(startTimeMillis + 12 * MILLIS_PER_SECOND)); + // 42s, add a new value value1, should fall into bucket [40.0, 42.5) + putToMeasureMap(statsRecorder.newMeasureMap(), measure, value6).record(tags); + + clock.setTime(Timestamp.fromMillis(startTimeMillis + 17 * MILLIS_PER_SECOND)); + // 47s, values in the first and second bucket should have expired, and 80% of values in the + // third bucket should have expired. The new value should persist. + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of(Arrays.asList(VALUE), expectedValues2), + EPSILON); + + clock.setTime(Timestamp.fromMillis(60 * MILLIS_PER_SECOND)); + // 60s, all previous values should have expired, add another value value2 + putToMeasureMap(statsRecorder.newMeasureMap(), measure, value7).record(tags); + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of(Arrays.asList(VALUE), expectedValues3), + EPSILON); + + clock.setTime(Timestamp.fromMillis(100 * MILLIS_PER_SECOND)); + // 100s, all values should have expired + assertThat(viewManager.getView(VIEW_NAME).getAggregationMap()).isEmpty(); + } + + @Test + public void getViewDoesNotClearStats() { + View view = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY)); + clock.setTime(Timestamp.create(10, 0)); + viewManager.registerView(view); + TagContext tags = tagger.emptyBuilder().put(KEY, VALUE).build(); + statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, 0.1).record(tags); + clock.setTime(Timestamp.create(11, 0)); + ViewData viewData1 = viewManager.getView(VIEW_NAME); + assertThat(viewData1.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(10, 0), Timestamp.create(11, 0))); + StatsTestUtil.assertAggregationMapEquals( + viewData1.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 0.1)), + EPSILON); + + statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, 0.2).record(tags); + clock.setTime(Timestamp.create(12, 0)); + ViewData viewData2 = viewManager.getView(VIEW_NAME); + + // The second view should have the same start time as the first view, and it should include both + // recorded values: + assertThat(viewData2.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(10, 0), Timestamp.create(12, 0))); + StatsTestUtil.assertAggregationMapEquals( + viewData2.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 0.1, 0.2)), + EPSILON); + } + + @Test + public void testRecordCumulativeMultipleTagValues() { + viewManager.registerView( + createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY))); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 10.0) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 30.0) + .record(tagger.emptyBuilder().put(KEY, VALUE_2).build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 50.0) + .record(tagger.emptyBuilder().put(KEY, VALUE_2).build()); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 10.0), + Arrays.asList(VALUE_2), + createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 30.0, 50.0)), + EPSILON); + } + + @Test + public void testRecordIntervalMultipleTagValues() { + // The interval is 10 seconds, i.e. values should expire after 10 seconds. + View view = + View.create( + VIEW_NAME, + VIEW_DESCRIPTION, + MEASURE_DOUBLE, + DISTRIBUTION, + Arrays.asList(KEY), + Interval.create(TEN_SECONDS)); + clock.setTime(Timestamp.create(10, 0)); // Start at 10s + viewManager.registerView(view); + + // record for TagValue1 at 11s + clock.setTime(Timestamp.fromMillis(11 * MILLIS_PER_SECOND)); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 10.0) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + + // record for TagValue2 at 15s + clock.setTime(Timestamp.fromMillis(15 * MILLIS_PER_SECOND)); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 30.0) + .record(tagger.emptyBuilder().put(KEY, VALUE_2).build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 50.0) + .record(tagger.emptyBuilder().put(KEY, VALUE_2).build()); + + // get ViewData at 19s, no stats should have expired. + clock.setTime(Timestamp.fromMillis(19 * MILLIS_PER_SECOND)); + ViewData viewData1 = viewManager.getView(VIEW_NAME); + StatsTestUtil.assertAggregationMapEquals( + viewData1.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 10.0), + Arrays.asList(VALUE_2), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 30.0, 50.0)), + EPSILON); + + // record for TagValue2 again at 20s + clock.setTime(Timestamp.fromMillis(20 * MILLIS_PER_SECOND)); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 40.0) + .record(tagger.emptyBuilder().put(KEY, VALUE_2).build()); + + // get ViewData at 25s, stats for TagValue1 should have expired. + clock.setTime(Timestamp.fromMillis(25 * MILLIS_PER_SECOND)); + ViewData viewData2 = viewManager.getView(VIEW_NAME); + StatsTestUtil.assertAggregationMapEquals( + viewData2.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE_2), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 30.0, 50.0, 40.0)), + EPSILON); + + // get ViewData at 30s, the first two values for TagValue2 should have expired. + clock.setTime(Timestamp.fromMillis(30 * MILLIS_PER_SECOND)); + ViewData viewData3 = viewManager.getView(VIEW_NAME); + StatsTestUtil.assertAggregationMapEquals( + viewData3.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE_2), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 40.0)), + EPSILON); + + // get ViewData at 40s, all stats should have expired. + clock.setTime(Timestamp.fromMillis(40 * MILLIS_PER_SECOND)); + ViewData viewData4 = viewManager.getView(VIEW_NAME); + assertThat(viewData4.getAggregationMap()).isEmpty(); + } + + // This test checks that MeasureMaper.record(...) does not throw an exception when no views are + // registered. + @Test + public void allowRecordingWithoutRegisteringMatchingViewData() { + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 10) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + } + + @Test + public void testRecordWithEmptyStatsContext() { + viewManager.registerView( + createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY))); + // DEFAULT doesn't have tags, but the view has tag key "KEY". + statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, 10.0).record(tagger.empty()); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + // Tag is missing for associated measureValues, should use default tag value + // "unknown/not set". + Arrays.asList(MutableViewData.UNKNOWN_TAG_VALUE), + // Should record stats with default tag value: "KEY" : "unknown/not set". + createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 10.0)), + EPSILON); + } + + @Test + public void testRecord_MeasureNameNotMatch() { + testRecord_MeasureNotMatch( + MeasureDouble.create(MEASURE_NAME, "measure", MEASURE_UNIT), + MeasureDouble.create(MEASURE_NAME_2, "measure", MEASURE_UNIT), + 10.0); + } + + @Test + public void testRecord_MeasureTypeNotMatch() { + testRecord_MeasureNotMatch( + MeasureLong.create(MEASURE_NAME, "measure", MEASURE_UNIT), + MeasureDouble.create(MEASURE_NAME, "measure", MEASURE_UNIT), + 10.0); + } + + private void testRecord_MeasureNotMatch(Measure measure1, Measure measure2, double value) { + viewManager.registerView(createCumulativeView(VIEW_NAME, measure1, MEAN, Arrays.asList(KEY))); + TagContext tags = tagger.emptyBuilder().put(KEY, VALUE).build(); + putToMeasureMap(statsRecorder.newMeasureMap(), measure2, value).record(tags); + ViewData view = viewManager.getView(VIEW_NAME); + assertThat(view.getAggregationMap()).isEmpty(); + } + + @Test + public void testRecordWithTagsThatDoNotMatchViewData() { + viewManager.registerView( + createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY))); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 10.0) + .record(tagger.emptyBuilder().put(TagKey.create("wrong key"), VALUE).build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 50.0) + .record(tagger.emptyBuilder().put(TagKey.create("another wrong key"), VALUE).build()); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + // Won't record the unregistered tag key, for missing registered keys will use default + // tag value : "unknown/not set". + Arrays.asList(MutableViewData.UNKNOWN_TAG_VALUE), + // Should record stats with default tag value: "KEY" : "unknown/not set". + createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 10.0, 50.0)), + EPSILON); + } + + @Test + public void testViewDataWithMultipleTagKeys() { + TagKey key1 = TagKey.create("Key-1"); + TagKey key2 = TagKey.create("Key-2"); + viewManager.registerView( + createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(key1, key2))); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record( + tagger + .emptyBuilder() + .put(key1, TagValue.create("v1")) + .put(key2, TagValue.create("v10")) + .build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 2.2) + .record( + tagger + .emptyBuilder() + .put(key1, TagValue.create("v1")) + .put(key2, TagValue.create("v20")) + .build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 3.3) + .record( + tagger + .emptyBuilder() + .put(key1, TagValue.create("v2")) + .put(key2, TagValue.create("v10")) + .build()); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 4.4) + .record( + tagger + .emptyBuilder() + .put(key1, TagValue.create("v1")) + .put(key2, TagValue.create("v10")) + .build()); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(TagValue.create("v1"), TagValue.create("v10")), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 1.1, 4.4), + Arrays.asList(TagValue.create("v1"), TagValue.create("v20")), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 2.2), + Arrays.asList(TagValue.create("v2"), TagValue.create("v10")), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 3.3)), + EPSILON); + } + + @Test + public void testMultipleViewSameMeasure() { + final View view1 = + createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY)); + final View view2 = + createCumulativeView(VIEW_NAME_2, MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY)); + clock.setTime(Timestamp.create(1, 1)); + viewManager.registerView(view1); + clock.setTime(Timestamp.create(2, 2)); + viewManager.registerView(view2); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 5.0) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + clock.setTime(Timestamp.create(3, 3)); + ViewData viewData1 = viewManager.getView(VIEW_NAME); + clock.setTime(Timestamp.create(4, 4)); + ViewData viewData2 = viewManager.getView(VIEW_NAME_2); + assertThat(viewData1.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(1, 1), Timestamp.create(3, 3))); + StatsTestUtil.assertAggregationMapEquals( + viewData1.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 5.0)), + EPSILON); + assertThat(viewData2.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(2, 2), Timestamp.create(4, 4))); + StatsTestUtil.assertAggregationMapEquals( + viewData2.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, MEASURE_DOUBLE, 5.0)), + EPSILON); + } + + @Test + public void testMultipleViews_DifferentMeasureNames() { + testMultipleViews_DifferentMeasures( + MeasureDouble.create(MEASURE_NAME, MEASURE_DESCRIPTION, MEASURE_UNIT), + MeasureDouble.create(MEASURE_NAME_2, MEASURE_DESCRIPTION, MEASURE_UNIT), + 1.1, + 2.2); + } + + @Test + public void testMultipleViews_DifferentMeasureTypes() { + testMultipleViews_DifferentMeasures( + MeasureDouble.create(MEASURE_NAME, MEASURE_DESCRIPTION, MEASURE_UNIT), + MeasureLong.create(MEASURE_NAME_2, MEASURE_DESCRIPTION, MEASURE_UNIT), + 1.1, + 5000); + } + + private void testMultipleViews_DifferentMeasures( + Measure measure1, Measure measure2, double value1, double value2) { + final View view1 = createCumulativeView(VIEW_NAME, measure1, DISTRIBUTION, Arrays.asList(KEY)); + final View view2 = + createCumulativeView(VIEW_NAME_2, measure2, DISTRIBUTION, Arrays.asList(KEY)); + clock.setTime(Timestamp.create(1, 0)); + viewManager.registerView(view1); + clock.setTime(Timestamp.create(2, 0)); + viewManager.registerView(view2); + TagContext tags = tagger.emptyBuilder().put(KEY, VALUE).build(); + MeasureMap measureMap = statsRecorder.newMeasureMap(); + putToMeasureMap(measureMap, measure1, value1); + putToMeasureMap(measureMap, measure2, value2); + measureMap.record(tags); + clock.setTime(Timestamp.create(3, 0)); + ViewData viewData1 = viewManager.getView(VIEW_NAME); + clock.setTime(Timestamp.create(4, 0)); + ViewData viewData2 = viewManager.getView(VIEW_NAME_2); + assertThat(viewData1.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(1, 0), Timestamp.create(3, 0))); + StatsTestUtil.assertAggregationMapEquals( + viewData1.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, measure1, value1)), + EPSILON); + assertThat(viewData2.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(2, 0), Timestamp.create(4, 0))); + StatsTestUtil.assertAggregationMapEquals( + viewData2.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(DISTRIBUTION, measure2, value2)), + EPSILON); + } + + @Test + public void testGetCumulativeViewDataWithEmptyBucketBoundaries() { + Aggregation noHistogram = + Distribution.create(BucketBoundaries.create(Collections.<Double>emptyList())); + View view = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, noHistogram, Arrays.asList(KEY)); + clock.setTime(Timestamp.create(1, 0)); + viewManager.registerView(view); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + clock.setTime(Timestamp.create(3, 0)); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertThat(viewData.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(1, 0), Timestamp.create(3, 0))); + StatsTestUtil.assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(noHistogram, MEASURE_DOUBLE, 1.1)), + EPSILON); + } + + @Test + public void testGetCumulativeViewDataWithoutBucketBoundaries() { + View view = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, MEAN, Arrays.asList(KEY)); + clock.setTime(Timestamp.create(1, 0)); + viewManager.registerView(view); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + clock.setTime(Timestamp.create(3, 0)); + ViewData viewData = viewManager.getView(VIEW_NAME); + assertThat(viewData.getWindowData()) + .isEqualTo(CumulativeData.create(Timestamp.create(1, 0), Timestamp.create(3, 0))); + StatsTestUtil.assertAggregationMapEquals( + viewData.getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), StatsTestUtil.createAggregationData(MEAN, MEASURE_DOUBLE, 1.1)), + EPSILON); + } + + @Test + public void registerRecordAndGetView_StatsDisabled() { + statsComponent.setState(StatsCollectionState.DISABLED); + View view = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, MEAN, Arrays.asList(KEY)); + viewManager.registerView(view); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + assertThat(viewManager.getView(VIEW_NAME)).isEqualTo(createEmptyViewData(view)); + } + + @Test + public void registerRecordAndGetView_StatsReenabled() { + statsComponent.setState(StatsCollectionState.DISABLED); + statsComponent.setState(StatsCollectionState.ENABLED); + View view = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, MEAN, Arrays.asList(KEY)); + viewManager.registerView(view); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), StatsTestUtil.createAggregationData(MEAN, MEASURE_DOUBLE, 1.1)), + EPSILON); + } + + @Test + public void registerViewWithStatsDisabled_RecordAndGetViewWithStatsEnabled() { + statsComponent.setState(StatsCollectionState.DISABLED); + View view = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, MEAN, Arrays.asList(KEY)); + viewManager.registerView(view); // view will still be registered. + + statsComponent.setState(StatsCollectionState.ENABLED); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(VIEW_NAME).getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), StatsTestUtil.createAggregationData(MEAN, MEASURE_DOUBLE, 1.1)), + EPSILON); + } + + @Test + public void registerDifferentViewWithSameNameWithStatsDisabled() { + statsComponent.setState(StatsCollectionState.DISABLED); + View view1 = + View.create( + VIEW_NAME, + "View description.", + MEASURE_DOUBLE, + DISTRIBUTION, + Arrays.asList(KEY), + CUMULATIVE); + View view2 = + View.create( + VIEW_NAME, + "This is a different description.", + MEASURE_DOUBLE, + DISTRIBUTION, + Arrays.asList(KEY), + CUMULATIVE); + testFailedToRegisterView( + view1, view2, "A different view with the same name is already registered"); + } + + @Test + public void settingStateToDisabledWillClearStats_Cumulative() { + View cumulativeView = createCumulativeView(VIEW_NAME, MEASURE_DOUBLE, MEAN, Arrays.asList(KEY)); + settingStateToDisabledWillClearStats(cumulativeView); + } + + @Test + public void settingStateToDisabledWillClearStats_Interval() { + View intervalView = + View.create( + VIEW_NAME_2, + VIEW_DESCRIPTION, + MEASURE_DOUBLE, + MEAN, + Arrays.asList(KEY), + Interval.create(Duration.create(60, 0))); + settingStateToDisabledWillClearStats(intervalView); + } + + private void settingStateToDisabledWillClearStats(View view) { + Timestamp timestamp1 = Timestamp.create(1, 0); + clock.setTime(timestamp1); + viewManager.registerView(view); + statsRecorder + .newMeasureMap() + .put(MEASURE_DOUBLE, 1.1) + .record(tagger.emptyBuilder().put(KEY, VALUE).build()); + StatsTestUtil.assertAggregationMapEquals( + viewManager.getView(view.getName()).getAggregationMap(), + ImmutableMap.of( + Arrays.asList(VALUE), + StatsTestUtil.createAggregationData(view.getAggregation(), view.getMeasure(), 1.1)), + EPSILON); + + Timestamp timestamp2 = Timestamp.create(2, 0); + clock.setTime(timestamp2); + statsComponent.setState(StatsCollectionState.DISABLED); // This will clear stats. + assertThat(viewManager.getView(view.getName())).isEqualTo(createEmptyViewData(view)); + + Timestamp timestamp3 = Timestamp.create(3, 0); + clock.setTime(timestamp3); + statsComponent.setState(StatsCollectionState.ENABLED); + + Timestamp timestamp4 = Timestamp.create(4, 0); + clock.setTime(timestamp4); + // This ViewData does not have any stats, but it should not be an empty ViewData, since it has + // non-zero TimeStamps. + ViewData viewData = viewManager.getView(view.getName()); + assertThat(viewData.getAggregationMap()).isEmpty(); + AggregationWindowData windowData = viewData.getWindowData(); + if (windowData instanceof CumulativeData) { + assertThat(windowData).isEqualTo(CumulativeData.create(timestamp3, timestamp4)); + } else { + assertThat(windowData).isEqualTo(IntervalData.create(timestamp4)); + } + } + + private static MeasureMap putToMeasureMap(MeasureMap measureMap, Measure measure, double value) { + if (measure instanceof MeasureDouble) { + return measureMap.put((MeasureDouble) measure, value); + } else if (measure instanceof MeasureLong) { + return measureMap.put((MeasureLong) measure, Math.round(value)); + } else { + // Future measures. + throw new AssertionError(); + } + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTagContextUtilsTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTagContextUtilsTest.java new file mode 100644 index 00000000..1a14ac6e --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTagContextUtilsTest.java @@ -0,0 +1,103 @@ +/* + * 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.tags; + +import static com.google.common.truth.Truth.assertThat; +import static io.opencensus.implcore.tags.TagsTestUtil.tagContextToList; + +import com.google.common.collect.ImmutableSet; +import io.grpc.Context; +import io.opencensus.common.Scope; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.unsafe.ContextUtils; +import java.util.Iterator; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link CurrentTagContextUtils}. */ +@RunWith(JUnit4.class) +public class CurrentTagContextUtilsTest { + private static final Tag TAG = Tag.create(TagKey.create("key"), TagValue.create("value")); + + private final TagContext tagContext = + new TagContext() { + + @Override + protected Iterator<Tag> getIterator() { + return ImmutableSet.<Tag>of(TAG).iterator(); + } + }; + + @Test + public void testGetCurrentTagContext_DefaultContext() { + TagContext tags = CurrentTagContextUtils.getCurrentTagContext(); + assertThat(tags).isNotNull(); + assertThat(tagContextToList(tags)).isEmpty(); + } + + @Test + public void testGetCurrentTagContext_ContextSetToNull() { + Context orig = Context.current().withValue(ContextUtils.TAG_CONTEXT_KEY, null).attach(); + try { + TagContext tags = CurrentTagContextUtils.getCurrentTagContext(); + assertThat(tags).isNotNull(); + assertThat(tagContextToList(tags)).isEmpty(); + } finally { + Context.current().detach(orig); + } + } + + @Test + public void testWithTagContext() { + assertThat(tagContextToList(CurrentTagContextUtils.getCurrentTagContext())).isEmpty(); + Scope scopedTags = CurrentTagContextUtils.withTagContext(tagContext); + try { + assertThat(CurrentTagContextUtils.getCurrentTagContext()).isSameAs(tagContext); + } finally { + scopedTags.close(); + } + assertThat(tagContextToList(CurrentTagContextUtils.getCurrentTagContext())).isEmpty(); + } + + @Test + public void testWithTagContextUsingWrap() { + Runnable runnable; + Scope scopedTags = CurrentTagContextUtils.withTagContext(tagContext); + try { + assertThat(CurrentTagContextUtils.getCurrentTagContext()).isSameAs(tagContext); + runnable = + Context.current() + .wrap( + new Runnable() { + @Override + public void run() { + assertThat(CurrentTagContextUtils.getCurrentTagContext()) + .isSameAs(tagContext); + } + }); + } finally { + scopedTags.close(); + } + assertThat(tagContextToList(CurrentTagContextUtils.getCurrentTagContext())).isEmpty(); + // When we run the runnable we will have the TagContext in the current Context. + runnable.run(); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTaggingStateTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTaggingStateTest.java new file mode 100644 index 00000000..244b6711 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTaggingStateTest.java @@ -0,0 +1,43 @@ +/* + * 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.tags; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.tags.TaggingState; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link CurrentTaggingState}. */ +@RunWith(JUnit4.class) +public final class CurrentTaggingStateTest { + + @Test + public void defaultState() { + assertThat(new CurrentTaggingState().get()).isEqualTo(TaggingState.ENABLED); + } + + @Test + public void setState() { + CurrentTaggingState state = new CurrentTaggingState(); + state.set(TaggingState.DISABLED); + assertThat(state.get()).isEqualTo(TaggingState.DISABLED); + state.set(TaggingState.ENABLED); + assertThat(state.get()).isEqualTo(TaggingState.ENABLED); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/ScopedTagContextsTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/ScopedTagContextsTest.java new file mode 100644 index 00000000..c24cccb9 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/ScopedTagContextsTest.java @@ -0,0 +1,110 @@ +/* + * 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.tags; + +import static com.google.common.truth.Truth.assertThat; +import static io.opencensus.implcore.tags.TagsTestUtil.tagContextToList; + +import io.opencensus.common.Scope; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for the methods in {@link TaggerImpl} and {@link TagContextBuilderImpl} that interact + * with the current {@link TagContext}. + */ +@RunWith(JUnit4.class) +public class ScopedTagContextsTest { + private static final TagKey KEY_1 = TagKey.create("key 1"); + private static final TagKey KEY_2 = TagKey.create("key 2"); + + private static final TagValue VALUE_1 = TagValue.create("value 1"); + private static final TagValue VALUE_2 = TagValue.create("value 2"); + + private final Tagger tagger = new TaggerImpl(new CurrentTaggingState()); + + @Test + public void defaultTagContext() { + TagContext defaultTagContext = tagger.getCurrentTagContext(); + assertThat(tagContextToList(defaultTagContext)).isEmpty(); + assertThat(defaultTagContext).isInstanceOf(TagContextImpl.class); + } + + @Test + public void withTagContext() { + assertThat(tagContextToList(tagger.getCurrentTagContext())).isEmpty(); + TagContext scopedTags = tagger.emptyBuilder().put(KEY_1, VALUE_1).build(); + Scope scope = tagger.withTagContext(scopedTags); + try { + assertThat(tagger.getCurrentTagContext()).isSameAs(scopedTags); + } finally { + scope.close(); + } + assertThat(tagContextToList(tagger.getCurrentTagContext())).isEmpty(); + } + + @Test + public void createBuilderFromCurrentTags() { + TagContext scopedTags = tagger.emptyBuilder().put(KEY_1, VALUE_1).build(); + Scope scope = tagger.withTagContext(scopedTags); + try { + TagContext newTags = tagger.currentBuilder().put(KEY_2, VALUE_2).build(); + assertThat(tagContextToList(newTags)) + .containsExactly(Tag.create(KEY_1, VALUE_1), Tag.create(KEY_2, VALUE_2)); + assertThat(tagger.getCurrentTagContext()).isSameAs(scopedTags); + } finally { + scope.close(); + } + } + + @Test + public void setCurrentTagsWithBuilder() { + assertThat(tagContextToList(tagger.getCurrentTagContext())).isEmpty(); + Scope scope = tagger.emptyBuilder().put(KEY_1, VALUE_1).buildScoped(); + try { + assertThat(tagContextToList(tagger.getCurrentTagContext())) + .containsExactly(Tag.create(KEY_1, VALUE_1)); + } finally { + scope.close(); + } + assertThat(tagContextToList(tagger.getCurrentTagContext())).isEmpty(); + } + + @Test + public void addToCurrentTagsWithBuilder() { + TagContext scopedTags = tagger.emptyBuilder().put(KEY_1, VALUE_1).build(); + Scope scope1 = tagger.withTagContext(scopedTags); + try { + Scope scope2 = tagger.currentBuilder().put(KEY_2, VALUE_2).buildScoped(); + try { + assertThat(tagContextToList(tagger.getCurrentTagContext())) + .containsExactly(Tag.create(KEY_1, VALUE_1), Tag.create(KEY_2, VALUE_2)); + } finally { + scope2.close(); + } + assertThat(tagger.getCurrentTagContext()).isSameAs(scopedTags); + } finally { + scope1.close(); + } + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/TagContextImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/TagContextImplTest.java new file mode 100644 index 00000000..b70d8b4e --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TagContextImplTest.java @@ -0,0 +1,112 @@ +/* + * 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.tags; + +import static com.google.common.truth.Truth.assertThat; +import static io.opencensus.implcore.tags.TagsTestUtil.tagContextToList; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.Lists; +import com.google.common.testing.EqualsTester; +import io.opencensus.tags.InternalUtils; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import java.util.Arrays; +import java.util.Iterator; +import java.util.NoSuchElementException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link TagContextImpl} and {@link TagContextBuilderImpl}. */ +// TODO(sebright): Add more tests once the API is finalized. +@RunWith(JUnit4.class) +public class TagContextImplTest { + private final Tagger tagger = new TaggerImpl(new CurrentTaggingState()); + + private static final TagKey KS1 = TagKey.create("k1"); + private static final TagKey KS2 = TagKey.create("k2"); + + private static final TagValue V1 = TagValue.create("v1"); + private static final TagValue V2 = TagValue.create("v2"); + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSet() { + TagContext tags = tagger.emptyBuilder().put(KS1, V1).build(); + assertThat(tagContextToList(tagger.toBuilder(tags).put(KS1, V2).build())) + .containsExactly(Tag.create(KS1, V2)); + assertThat(tagContextToList(tagger.toBuilder(tags).put(KS2, V2).build())) + .containsExactly(Tag.create(KS1, V1), Tag.create(KS2, V2)); + } + + @Test + public void testClear() { + TagContext tags = tagger.emptyBuilder().put(KS1, V1).build(); + assertThat(tagContextToList(tagger.toBuilder(tags).remove(KS1).build())).isEmpty(); + assertThat(tagContextToList(tagger.toBuilder(tags).remove(KS2).build())) + .containsExactly(Tag.create(KS1, V1)); + } + + @Test + public void testIterator() { + TagContext tags = tagger.emptyBuilder().put(KS1, V1).put(KS2, V2).build(); + Iterator<Tag> i = InternalUtils.getTags(tags); + assertTrue(i.hasNext()); + Tag tag1 = i.next(); + assertTrue(i.hasNext()); + Tag tag2 = i.next(); + assertFalse(i.hasNext()); + assertThat(Arrays.asList(tag1, tag2)).containsExactly(Tag.create(KS1, V1), Tag.create(KS2, V2)); + thrown.expect(NoSuchElementException.class); + i.next(); + } + + @Test + public void disallowCallingRemoveOnIterator() { + TagContext tags = tagger.emptyBuilder().put(KS1, V1).put(KS2, V2).build(); + Iterator<Tag> i = InternalUtils.getTags(tags); + i.next(); + thrown.expect(UnsupportedOperationException.class); + i.remove(); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + tagger.emptyBuilder().put(KS1, V1).put(KS2, V2).build(), + tagger.emptyBuilder().put(KS1, V1).put(KS2, V2).build(), + tagger.emptyBuilder().put(KS2, V2).put(KS1, V1).build(), + new TagContext() { + @Override + protected Iterator<Tag> getIterator() { + return Lists.<Tag>newArrayList(Tag.create(KS1, V1), Tag.create(KS2, V2)).iterator(); + } + }) + .addEqualityGroup(tagger.emptyBuilder().put(KS1, V1).put(KS2, V1).build()) + .addEqualityGroup(tagger.emptyBuilder().put(KS1, V2).put(KS2, V1).build()) + .testEquals(); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/TaggerImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/TaggerImplTest.java new file mode 100644 index 00000000..4ca2ae76 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TaggerImplTest.java @@ -0,0 +1,318 @@ +/* + * 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.tags; + +import static com.google.common.truth.Truth.assertThat; +import static io.opencensus.implcore.tags.TagsTestUtil.tagContextToList; + +import com.google.common.collect.Lists; +import io.grpc.Context; +import io.opencensus.common.Scope; +import io.opencensus.implcore.internal.NoopScope; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagContextBuilder; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opencensus.tags.TaggingState; +import io.opencensus.tags.TagsComponent; +import io.opencensus.tags.unsafe.ContextUtils; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link TaggerImpl}. */ +@RunWith(JUnit4.class) +public class TaggerImplTest { + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + private final Tagger tagger = tagsComponent.getTagger(); + + private static final TagKey K1 = TagKey.create("k1"); + private static final TagKey K2 = TagKey.create("k2"); + private static final TagKey K3 = TagKey.create("k3"); + + private static final TagValue V1 = TagValue.create("v1"); + private static final TagValue V2 = TagValue.create("v2"); + private static final TagValue V3 = TagValue.create("v3"); + + private static final Tag TAG1 = Tag.create(K1, V1); + private static final Tag TAG2 = Tag.create(K2, V2); + private static final Tag TAG3 = Tag.create(K3, V3); + + @Test + public void empty() { + assertThat(tagContextToList(tagger.empty())).isEmpty(); + assertThat(tagger.empty()).isInstanceOf(TagContextImpl.class); + } + + @Test + public void empty_TaggingDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagContextToList(tagger.empty())).isEmpty(); + assertThat(tagger.empty()).isInstanceOf(TagContextImpl.class); + } + + @Test + public void emptyBuilder() { + TagContextBuilder builder = tagger.emptyBuilder(); + assertThat(builder).isInstanceOf(TagContextBuilderImpl.class); + assertThat(tagContextToList(builder.build())).isEmpty(); + } + + @Test + public void emptyBuilder_TaggingDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagger.emptyBuilder()).isSameAs(NoopTagContextBuilder.INSTANCE); + } + + @Test + public void emptyBuilder_TaggingReenabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagger.emptyBuilder()).isSameAs(NoopTagContextBuilder.INSTANCE); + tagsComponent.setState(TaggingState.ENABLED); + TagContextBuilder builder = tagger.emptyBuilder(); + assertThat(builder).isInstanceOf(TagContextBuilderImpl.class); + assertThat(tagContextToList(builder.put(K1, V1).build())).containsExactly(Tag.create(K1, V1)); + } + + @Test + public void currentBuilder() { + TagContext tags = new SimpleTagContext(TAG1, TAG2, TAG3); + TagContextBuilder result = getResultOfCurrentBuilder(tags); + assertThat(result).isInstanceOf(TagContextBuilderImpl.class); + assertThat(tagContextToList(result.build())).containsExactly(TAG1, TAG2, TAG3); + } + + @Test + public void currentBuilder_DefaultIsEmpty() { + TagContextBuilder currentBuilder = tagger.currentBuilder(); + assertThat(currentBuilder).isInstanceOf(TagContextBuilderImpl.class); + assertThat(tagContextToList(currentBuilder.build())).isEmpty(); + } + + @Test + public void currentBuilder_RemoveDuplicateTags() { + Tag tag1 = Tag.create(K1, V1); + Tag tag2 = Tag.create(K1, V2); + TagContext tagContextWithDuplicateTags = new SimpleTagContext(tag1, tag2); + TagContextBuilder result = getResultOfCurrentBuilder(tagContextWithDuplicateTags); + assertThat(tagContextToList(result.build())).containsExactly(tag2); + } + + @Test + public void currentBuilder_SkipNullTag() { + TagContext tagContextWithNullTag = new SimpleTagContext(TAG1, null, TAG2); + TagContextBuilder result = getResultOfCurrentBuilder(tagContextWithNullTag); + assertThat(tagContextToList(result.build())).containsExactly(TAG1, TAG2); + } + + @Test + public void currentBuilder_TaggingDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(getResultOfCurrentBuilder(new SimpleTagContext(TAG1))) + .isSameAs(NoopTagContextBuilder.INSTANCE); + } + + @Test + public void currentBuilder_TaggingReenabled() { + TagContext tags = new SimpleTagContext(TAG1); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(getResultOfCurrentBuilder(tags)).isSameAs(NoopTagContextBuilder.INSTANCE); + tagsComponent.setState(TaggingState.ENABLED); + TagContextBuilder builder = getResultOfCurrentBuilder(tags); + assertThat(builder).isInstanceOf(TagContextBuilderImpl.class); + assertThat(tagContextToList(builder.build())).containsExactly(TAG1); + } + + private TagContextBuilder getResultOfCurrentBuilder(TagContext tagsToSet) { + Context orig = Context.current().withValue(ContextUtils.TAG_CONTEXT_KEY, tagsToSet).attach(); + try { + return tagger.currentBuilder(); + } finally { + Context.current().detach(orig); + } + } + + @Test + public void toBuilder_ConvertUnknownTagContextToTagContextImpl() { + TagContext unknownTagContext = new SimpleTagContext(TAG1, TAG2, TAG3); + TagContext newTagContext = tagger.toBuilder(unknownTagContext).build(); + assertThat(tagContextToList(newTagContext)).containsExactly(TAG1, TAG2, TAG3); + assertThat(newTagContext).isInstanceOf(TagContextImpl.class); + } + + @Test + public void toBuilder_RemoveDuplicatesFromUnknownTagContext() { + Tag tag1 = Tag.create(K1, V1); + Tag tag2 = Tag.create(K1, V2); + TagContext tagContextWithDuplicateTags = new SimpleTagContext(tag1, tag2); + TagContext newTagContext = tagger.toBuilder(tagContextWithDuplicateTags).build(); + assertThat(tagContextToList(newTagContext)).containsExactly(tag2); + } + + @Test + public void toBuilder_SkipNullTag() { + TagContext tagContextWithNullTag = new SimpleTagContext(TAG1, null, TAG2); + TagContext newTagContext = tagger.toBuilder(tagContextWithNullTag).build(); + assertThat(tagContextToList(newTagContext)).containsExactly(TAG1, TAG2); + } + + @Test + public void toBuilder_TaggingDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagger.toBuilder(new SimpleTagContext(TAG1))) + .isSameAs(NoopTagContextBuilder.INSTANCE); + } + + @Test + public void toBuilder_TaggingReenabled() { + TagContext tags = new SimpleTagContext(TAG1); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagger.toBuilder(tags)).isSameAs(NoopTagContextBuilder.INSTANCE); + tagsComponent.setState(TaggingState.ENABLED); + TagContextBuilder builder = tagger.toBuilder(tags); + assertThat(builder).isInstanceOf(TagContextBuilderImpl.class); + assertThat(tagContextToList(builder.build())).containsExactly(TAG1); + } + + @Test + public void getCurrentTagContext_DefaultIsEmptyTagContextImpl() { + TagContext currentTagContext = tagger.getCurrentTagContext(); + assertThat(tagContextToList(currentTagContext)).isEmpty(); + assertThat(currentTagContext).isInstanceOf(TagContextImpl.class); + } + + @Test + public void getCurrentTagContext_ConvertUnknownTagContextToTagContextImpl() { + TagContext unknownTagContext = new SimpleTagContext(TAG1, TAG2, TAG3); + TagContext result = getResultOfGetCurrentTagContext(unknownTagContext); + assertThat(result).isInstanceOf(TagContextImpl.class); + assertThat(tagContextToList(result)).containsExactly(TAG1, TAG2, TAG3); + } + + @Test + public void getCurrentTagContext_RemoveDuplicatesFromUnknownTagContext() { + Tag tag1 = Tag.create(K1, V1); + Tag tag2 = Tag.create(K1, V2); + TagContext tagContextWithDuplicateTags = new SimpleTagContext(tag1, tag2); + TagContext result = getResultOfGetCurrentTagContext(tagContextWithDuplicateTags); + assertThat(tagContextToList(result)).containsExactly(tag2); + } + + @Test + public void getCurrentTagContext_SkipNullTag() { + TagContext tagContextWithNullTag = new SimpleTagContext(TAG1, null, TAG2); + TagContext result = getResultOfGetCurrentTagContext(tagContextWithNullTag); + assertThat(tagContextToList(result)).containsExactly(TAG1, TAG2); + } + + @Test + public void getCurrentTagContext_TaggingDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagContextToList(getResultOfGetCurrentTagContext(new SimpleTagContext(TAG1)))) + .isEmpty(); + } + + @Test + public void getCurrentTagContext_TaggingReenabled() { + TagContext tags = new SimpleTagContext(TAG1); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagContextToList(getResultOfGetCurrentTagContext(tags))).isEmpty(); + tagsComponent.setState(TaggingState.ENABLED); + assertThat(tagContextToList(getResultOfGetCurrentTagContext(tags))).containsExactly(TAG1); + } + + private TagContext getResultOfGetCurrentTagContext(TagContext tagsToSet) { + Context orig = Context.current().withValue(ContextUtils.TAG_CONTEXT_KEY, tagsToSet).attach(); + try { + return tagger.getCurrentTagContext(); + } finally { + Context.current().detach(orig); + } + } + + @Test + public void withTagContext_ConvertUnknownTagContextToTagContextImpl() { + TagContext unknownTagContext = new SimpleTagContext(TAG1, TAG2, TAG3); + TagContext result = getResultOfWithTagContext(unknownTagContext); + assertThat(result).isInstanceOf(TagContextImpl.class); + assertThat(tagContextToList(result)).containsExactly(TAG1, TAG2, TAG3); + } + + @Test + public void withTagContext_RemoveDuplicatesFromUnknownTagContext() { + Tag tag1 = Tag.create(K1, V1); + Tag tag2 = Tag.create(K1, V2); + TagContext tagContextWithDuplicateTags = new SimpleTagContext(tag1, tag2); + TagContext result = getResultOfWithTagContext(tagContextWithDuplicateTags); + assertThat(tagContextToList(result)).containsExactly(tag2); + } + + @Test + public void withTagContext_SkipNullTag() { + TagContext tagContextWithNullTag = new SimpleTagContext(TAG1, null, TAG2); + TagContext result = getResultOfWithTagContext(tagContextWithNullTag); + assertThat(tagContextToList(result)).containsExactly(TAG1, TAG2); + } + + @Test + public void withTagContext_ReturnsNoopScopeWhenTaggingIsDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagger.withTagContext(new SimpleTagContext(TAG1))).isSameAs(NoopScope.getInstance()); + } + + @Test + public void withTagContext_TaggingDisabled() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagContextToList(getResultOfWithTagContext(new SimpleTagContext(TAG1)))).isEmpty(); + } + + @Test + public void withTagContext_TaggingReenabled() { + TagContext tags = new SimpleTagContext(TAG1); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagContextToList(getResultOfWithTagContext(tags))).isEmpty(); + tagsComponent.setState(TaggingState.ENABLED); + assertThat(tagContextToList(getResultOfWithTagContext(tags))).containsExactly(TAG1); + } + + private TagContext getResultOfWithTagContext(TagContext tagsToSet) { + Scope scope = tagger.withTagContext(tagsToSet); + try { + return ContextUtils.TAG_CONTEXT_KEY.get(); + } finally { + scope.close(); + } + } + + private static final class SimpleTagContext extends TagContext { + private final List<Tag> tags; + + SimpleTagContext(Tag... tags) { + this.tags = Collections.unmodifiableList(Lists.newArrayList(tags)); + } + + @Override + protected Iterator<Tag> getIterator() { + return tags.iterator(); + } + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/TagsComponentImplBaseTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/TagsComponentImplBaseTest.java new file mode 100644 index 00000000..e75a86d0 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TagsComponentImplBaseTest.java @@ -0,0 +1,49 @@ +/* + * 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.tags; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.tags.TaggingState; +import io.opencensus.tags.TagsComponent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link TagsComponentImplBase}. */ +@RunWith(JUnit4.class) +public class TagsComponentImplBaseTest { + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + + @Test + public void defaultState() { + assertThat(tagsComponent.getState()).isEqualTo(TaggingState.ENABLED); + } + + @Test + public void setState() { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(tagsComponent.getState()).isEqualTo(TaggingState.DISABLED); + tagsComponent.setState(TaggingState.ENABLED); + assertThat(tagsComponent.getState()).isEqualTo(TaggingState.ENABLED); + } + + @Test(expected = NullPointerException.class) + public void setState_DisallowsNull() { + tagsComponent.setState(null); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/TagsTestUtil.java b/impl_core/src/test/java/io/opencensus/implcore/tags/TagsTestUtil.java new file mode 100644 index 00000000..c64eced1 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TagsTestUtil.java @@ -0,0 +1,32 @@ +/* + * 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.tags; + +import com.google.common.collect.Lists; +import io.opencensus.tags.InternalUtils; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import java.util.Collection; + +/** Test utilities for tagging. */ +public class TagsTestUtil { + + /** Returns a collection of all tags in a {@link TagContext}. */ + public static Collection<Tag> tagContextToList(TagContext tags) { + return Lists.newArrayList(InternalUtils.getTags(tags)); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextBinarySerializerImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextBinarySerializerImplTest.java new file mode 100644 index 00000000..7e9bead1 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextBinarySerializerImplTest.java @@ -0,0 +1,91 @@ +/* + * 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.tags.propagation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.collect.ImmutableSet; +import io.opencensus.implcore.tags.TagsComponentImplBase; +import io.opencensus.implcore.tags.TagsTestUtil; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.TaggingState; +import io.opencensus.tags.TagsComponent; +import io.opencensus.tags.propagation.TagContextBinarySerializer; +import io.opencensus.tags.propagation.TagContextDeserializationException; +import io.opencensus.tags.propagation.TagContextSerializationException; +import java.util.Iterator; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for {@link TagContextBinarySerializerImpl}. + * + * <p>Thorough serialization/deserialization tests are in {@link TagContextSerializationTest}, + * {@link TagContextDeserializationTest}, and {@link TagContextRoundtripTest}. + */ +@RunWith(JUnit4.class) +public final class TagContextBinarySerializerImplTest { + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + private final TagContextBinarySerializer serializer = + tagsComponent.getTagPropagationComponent().getBinarySerializer(); + + private final TagContext tagContext = + new TagContext() { + @Override + public Iterator<Tag> getIterator() { + return ImmutableSet.<Tag>of(Tag.create(TagKey.create("key"), TagValue.create("value"))) + .iterator(); + } + }; + + @Test + public void toByteArray_TaggingDisabled() throws TagContextSerializationException { + tagsComponent.setState(TaggingState.DISABLED); + assertThat(serializer.toByteArray(tagContext)).isEmpty(); + } + + @Test + public void toByteArray_TaggingReenabled() throws TagContextSerializationException { + final byte[] serialized = serializer.toByteArray(tagContext); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(serializer.toByteArray(tagContext)).isEmpty(); + tagsComponent.setState(TaggingState.ENABLED); + assertThat(serializer.toByteArray(tagContext)).isEqualTo(serialized); + } + + @Test + public void fromByteArray_TaggingDisabled() + throws TagContextDeserializationException, TagContextSerializationException { + byte[] serialized = serializer.toByteArray(tagContext); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(TagsTestUtil.tagContextToList(serializer.fromByteArray(serialized))).isEmpty(); + } + + @Test + public void fromByteArray_TaggingReenabled() + throws TagContextDeserializationException, TagContextSerializationException { + byte[] serialized = serializer.toByteArray(tagContext); + tagsComponent.setState(TaggingState.DISABLED); + assertThat(TagsTestUtil.tagContextToList(serializer.fromByteArray(serialized))).isEmpty(); + tagsComponent.setState(TaggingState.ENABLED); + assertThat(serializer.fromByteArray(serialized)).isEqualTo(tagContext); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextDeserializationTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextDeserializationTest.java new file mode 100644 index 00000000..14118be0 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextDeserializationTest.java @@ -0,0 +1,199 @@ +/* + * Copyright 2016-17, 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.tags.propagation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Charsets; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import io.opencensus.implcore.internal.VarInt; +import io.opencensus.implcore.tags.TagsComponentImplBase; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opencensus.tags.TagsComponent; +import io.opencensus.tags.propagation.TagContextBinarySerializer; +import io.opencensus.tags.propagation.TagContextDeserializationException; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for deserializing tags with {@link SerializationUtils} and {@link + * TagContextBinarySerializerImpl}. + */ +@RunWith(JUnit4.class) +public class TagContextDeserializationTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + private final TagContextBinarySerializer serializer = + tagsComponent.getTagPropagationComponent().getBinarySerializer(); + private final Tagger tagger = tagsComponent.getTagger(); + + @Test + public void testVersionAndValueTypeConstants() { + // Refer to the JavaDoc on SerializationUtils for the definitions on these constants. + assertThat(SerializationUtils.VERSION_ID).isEqualTo(0); + assertThat(SerializationUtils.TAG_FIELD_ID).isEqualTo(0); + } + + @Test + public void testDeserializeNoTags() throws TagContextDeserializationException { + TagContext expected = tagger.empty(); + TagContext actual = + serializer.fromByteArray( + new byte[] {SerializationUtils.VERSION_ID}); // One byte that represents Version ID. + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testDeserializeEmptyByteArrayThrowException() + throws TagContextDeserializationException { + thrown.expect(TagContextDeserializationException.class); + thrown.expectMessage("Input byte[] can not be empty."); + serializer.fromByteArray(new byte[0]); + } + + @Test + public void testDeserializeInvalidTagKey() throws TagContextDeserializationException { + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.write(SerializationUtils.VERSION_ID); + + // Encode an invalid tag key and a valid tag value: + encodeTagToOutput("\2key", "value", output); + final byte[] bytes = output.toByteArray(); + + thrown.expect(TagContextDeserializationException.class); + thrown.expectMessage("Invalid tag key: \2key"); + serializer.fromByteArray(bytes); + } + + @Test + public void testDeserializeInvalidTagValue() throws TagContextDeserializationException { + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.write(SerializationUtils.VERSION_ID); + + // Encode a valid tag key and an invalid tag value: + encodeTagToOutput("my key", "val\3", output); + final byte[] bytes = output.toByteArray(); + + thrown.expect(TagContextDeserializationException.class); + thrown.expectMessage("Invalid tag value for key TagKey{name=my key}: val\3"); + serializer.fromByteArray(bytes); + } + + @Test + public void testDeserializeOneTag() throws TagContextDeserializationException { + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.write(SerializationUtils.VERSION_ID); + encodeTagToOutput("Key", "Value", output); + TagContext expected = + tagger.emptyBuilder().put(TagKey.create("Key"), TagValue.create("Value")).build(); + assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(expected); + } + + @Test + public void testDeserializeMultipleTags() throws TagContextDeserializationException { + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.write(SerializationUtils.VERSION_ID); + encodeTagToOutput("Key1", "Value1", output); + encodeTagToOutput("Key2", "Value2", output); + TagContext expected = + tagger + .emptyBuilder() + .put(TagKey.create("Key1"), TagValue.create("Value1")) + .put(TagKey.create("Key2"), TagValue.create("Value2")) + .build(); + assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(expected); + } + + @Test + public void stopParsingAtUnknownField() throws TagContextDeserializationException { + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.write(SerializationUtils.VERSION_ID); + encodeTagToOutput("Key1", "Value1", output); + encodeTagToOutput("Key2", "Value2", output); + + // Write unknown field ID 1. + output.write(1); + output.write(new byte[] {1, 2, 3, 4}); + + encodeTagToOutput("Key3", "Value3", output); + + // key 3 should not be included + TagContext expected = + tagger + .emptyBuilder() + .put(TagKey.create("Key1"), TagValue.create("Value1")) + .put(TagKey.create("Key2"), TagValue.create("Value2")) + .build(); + assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(expected); + } + + @Test + public void stopParsingAtUnknownTagAtStart() throws TagContextDeserializationException { + ByteArrayDataOutput output = ByteStreams.newDataOutput(); + output.write(SerializationUtils.VERSION_ID); + + // Write unknown field ID 1. + output.write(1); + output.write(new byte[] {1, 2, 3, 4}); + + encodeTagToOutput("Key", "Value", output); + assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(tagger.empty()); + } + + @Test + public void testDeserializeWrongFormat() throws TagContextDeserializationException { + // encoded tags should follow the format <version_id>(<tag_field_id><tag_encoding>)* + thrown.expect(TagContextDeserializationException.class); + serializer.fromByteArray(new byte[3]); + } + + @Test + public void testDeserializeWrongVersionId() throws TagContextDeserializationException { + thrown.expect(TagContextDeserializationException.class); + thrown.expectMessage("Wrong Version ID: 1. Currently supported version is: 0"); + serializer.fromByteArray(new byte[] {(byte) (SerializationUtils.VERSION_ID + 1)}); + } + + // <tag_encoding> == + // <tag_key_len><tag_key><tag_val_len><tag_val> + // <tag_key_len> == varint encoded integer + // <tag_key> == tag_key_len bytes comprising tag key name + // <tag_val_len> == varint encoded integer + // <tag_val> == tag_val_len bytes comprising UTF-8 string + private static void encodeTagToOutput(String key, String value, ByteArrayDataOutput output) { + output.write(SerializationUtils.TAG_FIELD_ID); + encodeString(key, output); + encodeString(value, output); + } + + private static void encodeString(String input, ByteArrayDataOutput output) { + int length = input.length(); + byte[] bytes = new byte[VarInt.varIntSize(length)]; + VarInt.putVarInt(length, bytes, 0); + output.write(bytes); + output.write(input.getBytes(Charsets.UTF_8)); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextRoundtripTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextRoundtripTest.java new file mode 100644 index 00000000..88abcc1d --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextRoundtripTest.java @@ -0,0 +1,63 @@ +/* + * 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.tags.propagation; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.implcore.tags.TagsComponentImplBase; +import io.opencensus.tags.TagContext; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opencensus.tags.TagsComponent; +import io.opencensus.tags.propagation.TagContextBinarySerializer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for roundtrip serialization with {@link TagContextBinarySerializerImpl}. */ +@RunWith(JUnit4.class) +public class TagContextRoundtripTest { + + private static final TagKey K1 = TagKey.create("k1"); + private static final TagKey K2 = TagKey.create("k2"); + private static final TagKey K3 = TagKey.create("k3"); + + private static final TagValue V_EMPTY = TagValue.create(""); + private static final TagValue V1 = TagValue.create("v1"); + private static final TagValue V2 = TagValue.create("v2"); + private static final TagValue V3 = TagValue.create("v3"); + + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + private final TagContextBinarySerializer serializer = + tagsComponent.getTagPropagationComponent().getBinarySerializer(); + private final Tagger tagger = tagsComponent.getTagger(); + + @Test + public void testRoundtripSerialization() throws Exception { + testRoundtripSerialization(tagger.empty()); + testRoundtripSerialization(tagger.emptyBuilder().put(K1, V1).build()); + testRoundtripSerialization(tagger.emptyBuilder().put(K1, V1).put(K2, V2).put(K3, V3).build()); + testRoundtripSerialization(tagger.emptyBuilder().put(K1, V_EMPTY).build()); + } + + private void testRoundtripSerialization(TagContext expected) throws Exception { + byte[] bytes = serializer.toByteArray(expected); + TagContext actual = serializer.fromByteArray(bytes); + assertThat(actual).isEqualTo(expected); + } +} diff --git a/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextSerializationTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextSerializationTest.java new file mode 100644 index 00000000..6227fe54 --- /dev/null +++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextSerializationTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2016-17, 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.tags.propagation; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.base.Charsets; +import com.google.common.collect.Collections2; +import io.opencensus.implcore.internal.VarInt; +import io.opencensus.implcore.tags.TagsComponentImplBase; +import io.opencensus.tags.Tag; +import io.opencensus.tags.TagContextBuilder; +import io.opencensus.tags.TagKey; +import io.opencensus.tags.TagValue; +import io.opencensus.tags.Tagger; +import io.opencensus.tags.TagsComponent; +import io.opencensus.tags.propagation.TagContextBinarySerializer; +import io.opencensus.tags.propagation.TagContextSerializationException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Tests for serializing tags with {@link SerializationUtils} and {@link + * TagContextBinarySerializerImpl}. + */ +@RunWith(JUnit4.class) +public class TagContextSerializationTest { + + private static final int VERSION_ID = 0; + private static final int TAG_FIELD_ID = 0; + + private static final TagKey K1 = TagKey.create("k1"); + private static final TagKey K2 = TagKey.create("k2"); + private static final TagKey K3 = TagKey.create("k3"); + private static final TagKey K4 = TagKey.create("k4"); + + private static final TagValue V1 = TagValue.create("v1"); + private static final TagValue V2 = TagValue.create("v2"); + private static final TagValue V3 = TagValue.create("v3"); + private static final TagValue V4 = TagValue.create("v4"); + + private static final Tag T1 = Tag.create(K1, V1); + private static final Tag T2 = Tag.create(K2, V2); + private static final Tag T3 = Tag.create(K3, V3); + private static final Tag T4 = Tag.create(K4, V4); + + private final TagsComponent tagsComponent = new TagsComponentImplBase(); + private final TagContextBinarySerializer serializer = + tagsComponent.getTagPropagationComponent().getBinarySerializer(); + private final Tagger tagger = tagsComponent.getTagger(); + + @Test + public void testSerializeDefault() throws Exception { + testSerialize(); + } + + @Test + public void testSerializeWithOneTag() throws Exception { + testSerialize(T1); + } + + @Test + public void testSerializeWithMultipleTags() throws Exception { + testSerialize(T1, T2, T3, T4); + } + + private void testSerialize(Tag... tags) throws IOException, TagContextSerializationException { + TagContextBuilder builder = tagger.emptyBuilder(); + for (Tag tag : tags) { + builder.put(tag.getKey(), tag.getValue()); + } + + byte[] actual = serializer.toByteArray(builder.build()); + + Collection<List<Tag>> tagPermutation = Collections2.permutations(Arrays.asList(tags)); + Set<String> possibleOutputs = new HashSet<String>(); + for (List<Tag> list : tagPermutation) { + ByteArrayOutputStream expected = new ByteArrayOutputStream(); + expected.write(VERSION_ID); + for (Tag tag : list) { + expected.write(TAG_FIELD_ID); + encodeString(tag.getKey().getName(), expected); + encodeString(tag.getValue().asString(), expected); + } + possibleOutputs.add(expected.toString()); + } + + assertThat(possibleOutputs).contains(new String(actual, Charsets.UTF_8)); + } + + private static void encodeString(String input, ByteArrayOutputStream byteArrayOutputStream) + throws IOException { + VarInt.putVarInt(input.length(), byteArrayOutputStream); + byteArrayOutputStream.write(input.getBytes(Charsets.UTF_8)); + } +} |