aboutsummaryrefslogtreecommitdiff
path: root/impl_core/src/test/java/io/opencensus
diff options
context:
space:
mode:
authorYang Song <songy23@users.noreply.github.com>2017-11-09 13:59:08 -0800
committerGitHub <noreply@github.com>2017-11-09 13:59:08 -0800
commit5923bdbee2ea12ad9c4fa962b5adf526d9fe839d (patch)
tree5eac15ce4178a53c1deb212b050c59034a9a6cfc /impl_core/src/test/java/io/opencensus
parent32cc8dd045a2502dffa792fd87ff9e994d8e7496 (diff)
downloadopencensus-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')
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/CurrentStatsStateTest.java65
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/IntervalBucketTest.java120
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MeasureMapInternalTest.java147
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MeasureToViewMapTest.java69
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MutableAggregationTest.java249
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MutableViewDataTest.java147
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/StatsComponentImplBaseTest.java73
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/StatsRecorderImplTest.java207
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/StatsTestUtil.java183
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java924
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTagContextUtilsTest.java103
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/CurrentTaggingStateTest.java43
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/ScopedTagContextsTest.java110
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/TagContextImplTest.java112
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/TaggerImplTest.java318
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/TagsComponentImplBaseTest.java49
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/TagsTestUtil.java32
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextBinarySerializerImplTest.java91
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextDeserializationTest.java199
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextRoundtripTest.java63
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextSerializationTest.java118
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));
+ }
+}