aboutsummaryrefslogtreecommitdiff
path: root/impl_core/src/test/java/io/opencensus
diff options
context:
space:
mode:
Diffstat (limited to 'impl_core/src/test/java/io/opencensus')
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/internal/CurrentStateTest.java57
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/internal/TimestampConverterTest.java51
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/internal/UtilsTest.java47
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImplTest.java221
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedLongGaugeImplTest.java224
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/DoubleGaugeImplTest.java292
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/LongGaugeImplTest.java287
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java356
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/MetricsComponentImplBaseTest.java53
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/export/ExportComponentImplTest.java35
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/metrics/export/MetricProducerManagerImplTest.java115
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/IntervalBucketTest.java133
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MeasureMapInternalTest.java159
-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/MetricUtilsTest.java129
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MutableAggregationTest.java339
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/MutableViewDataTest.java34
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/RecordUtilsTest.java116
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/StatsComponentImplBaseTest.java77
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/StatsRecorderImplTest.java349
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/StatsTestUtil.java232
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java1021
-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/ScopedTagContextsTest.java112
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/TagContextImplTest.java167
-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.java84
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/TagsTestUtil.java33
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextBinarySerializerImplTest.java94
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextDeserializationTest.java329
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextRoundtripTest.java85
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextSerializationTest.java147
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/NoRecordEventsSpanImplTest.java91
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/RecordEventsSpanImplTest.java594
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/SpanBuilderImplTest.java404
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/TraceComponentImplBaseTest.java57
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/TracerImplTest.java61
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/config/TraceConfigImplTest.java53
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/export/ExportComponentImplTest.java55
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessRunningSpanStoreImplTest.java168
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessSampledSpanStoreImplTest.java368
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopRunningSpanStoreImplTest.java95
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopSampledSpanStoreImplTest.java118
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/export/SpanExporterImplTest.java233
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveListTest.java123
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java221
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/propagation/BinaryFormatImplTest.java191
-rw-r--r--impl_core/src/test/java/io/opencensus/implcore/trace/propagation/PropagationComponentImplTest.java40
48 files changed, 8740 insertions, 0 deletions
diff --git a/impl_core/src/test/java/io/opencensus/implcore/internal/CurrentStateTest.java b/impl_core/src/test/java/io/opencensus/implcore/internal/CurrentStateTest.java
new file mode 100644
index 00000000..b7e6a93a
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/internal/CurrentStateTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018, 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.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.implcore.internal.CurrentState.State;
+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 CurrentState}. */
+@RunWith(JUnit4.class)
+public final class CurrentStateTest {
+
+ @Rule public final ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void defaultState() {
+ assertThat(new CurrentState(State.ENABLED).get()).isEqualTo(State.ENABLED);
+ }
+
+ @Test
+ public void setState() {
+ CurrentState currentState = new CurrentState(State.ENABLED);
+ assertThat(currentState.set(State.DISABLED)).isTrue();
+ assertThat(currentState.getInternal()).isEqualTo(State.DISABLED);
+ assertThat(currentState.set(State.ENABLED)).isTrue();
+ assertThat(currentState.getInternal()).isEqualTo(State.ENABLED);
+ assertThat(currentState.set(State.ENABLED)).isFalse();
+ }
+
+ @Test
+ public void preventSettingStateAfterReadingState() {
+ CurrentState currentState = new CurrentState(State.ENABLED);
+ currentState.get();
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("State was already read, cannot set state.");
+ currentState.set(State.DISABLED);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/internal/TimestampConverterTest.java b/impl_core/src/test/java/io/opencensus/implcore/internal/TimestampConverterTest.java
new file mode 100644
index 00000000..32a3e687
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/internal/TimestampConverterTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import io.opencensus.common.Clock;
+import io.opencensus.common.Timestamp;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link TimestampConverter}. */
+@RunWith(JUnit4.class)
+public class TimestampConverterTest {
+ private final Timestamp timestamp = Timestamp.create(1234, 5678);
+ @Mock private Clock mockClock;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void convertNanoTime() {
+ when(mockClock.now()).thenReturn(timestamp);
+ when(mockClock.nowNanos()).thenReturn(1234L);
+ TimestampConverter timeConverter = TimestampConverter.now(mockClock);
+ assertThat(timeConverter.convertNanoTime(6234)).isEqualTo(Timestamp.create(1234, 10678));
+ assertThat(timeConverter.convertNanoTime(1000)).isEqualTo(Timestamp.create(1234, 5444));
+ assertThat(timeConverter.convertNanoTime(999995556)).isEqualTo(Timestamp.create(1235, 0));
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/internal/UtilsTest.java b/impl_core/src/test/java/io/opencensus/implcore/internal/UtilsTest.java
new file mode 100644
index 00000000..2e0bde21
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/internal/UtilsTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018, 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.internal;
+
+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 Utils}. */
+@RunWith(JUnit4.class)
+public class UtilsTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void checkListElementNull() {
+ List<Double> list = Arrays.asList(0.0, 1.0, 2.0, null);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("null");
+ Utils.checkListElementNotNull(list, null);
+ }
+
+ @Test
+ public void checkListElementNull_WithMessage() {
+ List<Double> list = Arrays.asList(0.0, 1.0, 2.0, null);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("list should not be null.");
+ Utils.checkListElementNotNull(list, "list should not be null.");
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImplTest.java
new file mode 100644
index 00000000..e69a284f
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedDoubleGaugeImplTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2018, 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.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Timestamp;
+import io.opencensus.common.ToDoubleFunction;
+import io.opencensus.metrics.LabelKey;
+import io.opencensus.metrics.LabelValue;
+import io.opencensus.metrics.export.Metric;
+import io.opencensus.metrics.export.MetricDescriptor;
+import io.opencensus.metrics.export.MetricDescriptor.Type;
+import io.opencensus.metrics.export.Point;
+import io.opencensus.metrics.export.TimeSeries;
+import io.opencensus.metrics.export.Value;
+import io.opencensus.testing.common.TestClock;
+import java.util.ArrayList;
+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;
+
+/** Unit tests for {@link DerivedDoubleGaugeImpl}. */
+@RunWith(JUnit4.class)
+public class DerivedDoubleGaugeImplTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ private static final String METRIC_NAME = "name";
+ private static final String METRIC_DESCRIPTION = "description";
+ private static final String METRIC_UNIT = "1";
+ private static final List<LabelKey> LABEL_KEY =
+ Collections.singletonList(LabelKey.create("key", "key description"));
+ private static final List<LabelValue> LABEL_VALUES =
+ Collections.singletonList(LabelValue.create("value"));
+ private static final List<LabelValue> LABEL_VALUES_1 =
+ Collections.singletonList(LabelValue.create("value1"));
+ private static final Timestamp TEST_TIME = Timestamp.create(1234, 123);
+ private final TestClock testClock = TestClock.create(TEST_TIME);
+ private static final MetricDescriptor METRIC_DESCRIPTOR =
+ MetricDescriptor.create(
+ METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, Type.GAUGE_DOUBLE, LABEL_KEY);
+
+ private final DerivedDoubleGaugeImpl derivedDoubleGauge =
+ new DerivedDoubleGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, LABEL_KEY);
+
+ // helper class
+ public static class QueueManager {
+ public double size() {
+ return 2.5;
+ }
+ }
+
+ private static final ToDoubleFunction<Object> doubleFunction =
+ new ToDoubleFunction<Object>() {
+ @Override
+ public double applyAsDouble(Object value) {
+ return 5.5;
+ }
+ };
+ private static final ToDoubleFunction<Object> negativeDoubleFunction =
+ new ToDoubleFunction<Object>() {
+ @Override
+ public double applyAsDouble(Object value) {
+ return -200.5;
+ }
+ };
+ private static final ToDoubleFunction<QueueManager> queueManagerFunction =
+ new ToDoubleFunction<QueueManager>() {
+ @Override
+ public double applyAsDouble(QueueManager queue) {
+ return queue.size();
+ }
+ };
+
+ @Test
+ public void createTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ derivedDoubleGauge.createTimeSeries(null, null, doubleFunction);
+ }
+
+ @Test
+ public void createTimeSeries_WithNullElement() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ List<LabelValue> labelValues = Arrays.asList(LabelValue.create("value1"), null);
+ DerivedDoubleGaugeImpl derivedDoubleGauge =
+ new DerivedDoubleGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValue element should not be null.");
+ derivedDoubleGauge.createTimeSeries(labelValues, null, doubleFunction);
+ }
+
+ @Test
+ public void createTimeSeries_WithInvalidLabelSize() {
+ List<LabelValue> labelValues =
+ Arrays.asList(LabelValue.create("value1"), LabelValue.create("value2"));
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Incorrect number of labels.");
+ derivedDoubleGauge.createTimeSeries(labelValues, null, doubleFunction);
+ }
+
+ @Test
+ public void createTimeSeries_WithNullFunction() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("function");
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, null);
+ }
+
+ @Test
+ public void createTimeSeries_WithObjFunction() {
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, new QueueManager(), queueManagerFunction);
+ Metric metric = derivedDoubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(2.5), TEST_TIME), null)));
+ }
+
+ @Test
+ public void createTimeSeries_WithSameLabel() {
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, new QueueManager(), queueManagerFunction);
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("A different time series with the same labels already exists.");
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, queueManagerFunction);
+ }
+
+ @Test
+ public void addTimeSeries_WithNullObj() {
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, negativeDoubleFunction);
+ Metric metric = derivedDoubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(-200.5), TEST_TIME), null)));
+ }
+
+ @Test
+ public void removeTimeSeries() {
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, doubleFunction);
+ Metric metric = derivedDoubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(1);
+ derivedDoubleGauge.removeTimeSeries(LABEL_VALUES);
+ assertThat(derivedDoubleGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void removeTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ derivedDoubleGauge.removeTimeSeries(null);
+ }
+
+ @Test
+ public void multipleMetrics_GetMetric() {
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, doubleFunction);
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES_1, new QueueManager(), queueManagerFunction);
+ List<TimeSeries> expectedTimeSeriesList = new ArrayList<TimeSeries>();
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(5.5), TEST_TIME), null));
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES_1, Point.create(Value.doubleValue(2.5), TEST_TIME), null));
+ Metric metric = derivedDoubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(2);
+ assertThat(metric.getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeriesList);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().get(0))
+ .isEqualTo(LabelValue.create("value"));
+ assertThat(metric.getTimeSeriesList().get(1).getLabelValues().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(1).getLabelValues().get(0))
+ .isEqualTo(LabelValue.create("value1"));
+ }
+
+ @Test
+ public void clear() {
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, doubleFunction);
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES_1, new QueueManager(), queueManagerFunction);
+ Metric metric = derivedDoubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(2);
+ derivedDoubleGauge.clear();
+ assertThat(derivedDoubleGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void empty_GetMetrics() {
+ assertThat(derivedDoubleGauge.getMetric(testClock)).isNull();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedLongGaugeImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedLongGaugeImplTest.java
new file mode 100644
index 00000000..ec9cad6c
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/DerivedLongGaugeImplTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2018, 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.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Timestamp;
+import io.opencensus.common.ToLongFunction;
+import io.opencensus.metrics.LabelKey;
+import io.opencensus.metrics.LabelValue;
+import io.opencensus.metrics.export.Metric;
+import io.opencensus.metrics.export.MetricDescriptor;
+import io.opencensus.metrics.export.MetricDescriptor.Type;
+import io.opencensus.metrics.export.Point;
+import io.opencensus.metrics.export.TimeSeries;
+import io.opencensus.metrics.export.Value;
+import io.opencensus.testing.common.TestClock;
+import java.util.ArrayList;
+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;
+
+/** Unit tests for {@link DerivedLongGaugeImpl}. */
+@RunWith(JUnit4.class)
+public class DerivedLongGaugeImplTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ private static final String METRIC_NAME = "name";
+ private static final String METRIC_DESCRIPTION = "description";
+ private static final String METRIC_UNIT = "1";
+ private static final List<LabelKey> LABEL_KEY =
+ Collections.singletonList(LabelKey.create("key", "key description"));
+ private static final List<LabelValue> LABEL_VALUES =
+ Collections.singletonList(LabelValue.create("value"));
+ private static final List<LabelValue> LABEL_VALUES_1 =
+ Collections.singletonList(LabelValue.create("value1"));
+
+ private static final Timestamp TEST_TIME = Timestamp.create(1234, 123);
+ private final TestClock testClock = TestClock.create(TEST_TIME);
+
+ private static final MetricDescriptor METRIC_DESCRIPTOR =
+ MetricDescriptor.create(
+ METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, Type.GAUGE_INT64, LABEL_KEY);
+
+ private final DerivedLongGaugeImpl derivedLongGauge =
+ new DerivedLongGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, LABEL_KEY);
+
+ // helper class
+ public static class QueueManager {
+ public long size() {
+ return 2;
+ }
+ }
+
+ private static final ToLongFunction<Object> longFunction =
+ new ToLongFunction<Object>() {
+ @Override
+ public long applyAsLong(Object value) {
+ return 5;
+ }
+ };
+ private static final ToLongFunction<Object> negativeLongFunction =
+ new ToLongFunction<Object>() {
+ @Override
+ public long applyAsLong(Object value) {
+ return -200;
+ }
+ };
+ private static final ToLongFunction<QueueManager> queueManagerFunction =
+ new ToLongFunction<QueueManager>() {
+ @Override
+ public long applyAsLong(QueueManager queue) {
+ return queue.size();
+ }
+ };
+
+ @Test
+ public void createTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ derivedLongGauge.createTimeSeries(null, null, longFunction);
+ }
+
+ @Test
+ public void createTimeSeries_WithNullElement() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ List<LabelValue> labelValues = Arrays.asList(LabelValue.create("value1"), null);
+
+ DerivedLongGaugeImpl derivedLongGauge =
+ new DerivedLongGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValue element should not be null.");
+ derivedLongGauge.createTimeSeries(labelValues, null, longFunction);
+ }
+
+ @Test
+ public void createTimeSeries_WithInvalidLabelSize() {
+ List<LabelValue> labelValues =
+ Arrays.asList(LabelValue.create("value1"), LabelValue.create("value2"));
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Incorrect number of labels.");
+ derivedLongGauge.createTimeSeries(labelValues, null, longFunction);
+ }
+
+ @Test
+ public void createTimeSeries_WithNullFunction() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("function");
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, null);
+ }
+
+ @Test
+ public void createTimeSeries_WithObjFunction() {
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, new QueueManager(), queueManagerFunction);
+
+ Metric metric = derivedLongGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(2), TEST_TIME), null)));
+ }
+
+ @Test
+ public void addTimeSeries_WithNullObj() {
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, negativeLongFunction);
+
+ Metric metric = derivedLongGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(-200), TEST_TIME), null)));
+ }
+
+ @Test
+ public void removeTimeSeries() {
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, longFunction);
+ Metric metric = derivedLongGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(1);
+
+ derivedLongGauge.removeTimeSeries(LABEL_VALUES);
+ assertThat(derivedLongGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void removeTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ derivedLongGauge.removeTimeSeries(null);
+ }
+
+ @Test
+ public void multipleMetrics_GetMetric() {
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, longFunction);
+ derivedLongGauge.createTimeSeries(LABEL_VALUES_1, new QueueManager(), queueManagerFunction);
+
+ List<TimeSeries> expectedTimeSeriesList = new ArrayList<TimeSeries>();
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(5), TEST_TIME), null));
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES_1, Point.create(Value.longValue(2), TEST_TIME), null));
+
+ Metric metric = derivedLongGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(2);
+ assertThat(metric.getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeriesList);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().get(0))
+ .isEqualTo(LabelValue.create("value"));
+ assertThat(metric.getTimeSeriesList().get(1).getLabelValues().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(1).getLabelValues().get(0))
+ .isEqualTo(LabelValue.create("value1"));
+ }
+
+ @Test
+ public void clear() {
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, longFunction);
+ derivedLongGauge.createTimeSeries(LABEL_VALUES_1, new QueueManager(), queueManagerFunction);
+
+ Metric metric = derivedLongGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(2);
+
+ derivedLongGauge.clear();
+ assertThat(derivedLongGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void empty_GetMetrics() {
+ assertThat(derivedLongGauge.getMetric(testClock)).isNull();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/DoubleGaugeImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/DoubleGaugeImplTest.java
new file mode 100644
index 00000000..b0899084
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/DoubleGaugeImplTest.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2018, 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.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.opencensus.implcore.metrics.DoubleGaugeImpl.UNSET_VALUE;
+
+import com.google.common.testing.EqualsTester;
+import io.opencensus.common.Timestamp;
+import io.opencensus.metrics.DoubleGauge.DoublePoint;
+import io.opencensus.metrics.LabelKey;
+import io.opencensus.metrics.LabelValue;
+import io.opencensus.metrics.export.Metric;
+import io.opencensus.metrics.export.MetricDescriptor;
+import io.opencensus.metrics.export.MetricDescriptor.Type;
+import io.opencensus.metrics.export.Point;
+import io.opencensus.metrics.export.TimeSeries;
+import io.opencensus.metrics.export.Value;
+import io.opencensus.testing.common.TestClock;
+import java.util.ArrayList;
+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;
+
+/** Unit tests for {@link DoubleGaugeImpl}. */
+@RunWith(JUnit4.class)
+public class DoubleGaugeImplTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ private static final String METRIC_NAME = "name";
+ private static final String METRIC_DESCRIPTION = "description";
+ private static final String METRIC_UNIT = "1";
+ private static final List<LabelKey> LABEL_KEY =
+ Collections.singletonList(LabelKey.create("key", "key description"));
+ private static final List<LabelValue> LABEL_VALUES =
+ Collections.singletonList(LabelValue.create("value"));
+ private static final List<LabelValue> LABEL_VALUES1 =
+ Collections.singletonList(LabelValue.create("value1"));
+ private static final List<LabelValue> DEFAULT_LABEL_VALUES =
+ Collections.singletonList(UNSET_VALUE);
+
+ private static final Timestamp TEST_TIME = Timestamp.create(1234, 123);
+ private final TestClock testClock = TestClock.create(TEST_TIME);
+ private static final MetricDescriptor METRIC_DESCRIPTOR =
+ MetricDescriptor.create(
+ METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, Type.GAUGE_DOUBLE, LABEL_KEY);
+ private final DoubleGaugeImpl doubleGauge =
+ new DoubleGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, LABEL_KEY);
+
+ @Test
+ public void getOrCreateTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ doubleGauge.getOrCreateTimeSeries(null);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries_WithNullElement() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ List<LabelValue> labelValues = Arrays.asList(LabelValue.create("value1"), null);
+
+ DoubleGaugeImpl doubleGauge =
+ new DoubleGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValue element should not be null.");
+ doubleGauge.getOrCreateTimeSeries(labelValues);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries_WithInvalidLabelSize() {
+ List<LabelValue> labelValues =
+ Arrays.asList(LabelValue.create("value1"), LabelValue.create("value2"));
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Incorrect number of labels.");
+ doubleGauge.getOrCreateTimeSeries(labelValues);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries() {
+ DoublePoint point = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ point.add(100);
+ DoublePoint point1 = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ point1.set(500);
+
+ Metric metric = doubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.create(
+ METRIC_DESCRIPTOR,
+ Collections.singletonList(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(500), TEST_TIME), null))));
+ assertThat(point).isSameAs(point1);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries_WithNegativePointValues() {
+ DoublePoint point = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ point.add(-100);
+ point.add(-33);
+
+ Metric metric = doubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getPoints().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getPoints().get(0).getValue())
+ .isEqualTo(Value.doubleValue(-133));
+ assertThat(metric.getTimeSeriesList().get(0).getPoints().get(0).getTimestamp())
+ .isEqualTo(TEST_TIME);
+ assertThat(metric.getTimeSeriesList().get(0).getStartTimestamp()).isNull();
+ }
+
+ @Test
+ public void getDefaultTimeSeries() {
+ DoublePoint point = doubleGauge.getDefaultTimeSeries();
+ point.add(100);
+ point.set(500);
+
+ DoublePoint point1 = doubleGauge.getDefaultTimeSeries();
+ point1.add(-100);
+
+ Metric metric = doubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.create(
+ METRIC_DESCRIPTOR,
+ Collections.singletonList(
+ TimeSeries.createWithOnePoint(
+ DEFAULT_LABEL_VALUES,
+ Point.create(Value.doubleValue(400), TEST_TIME),
+ null))));
+ assertThat(point).isSameAs(point1);
+ }
+
+ @Test
+ public void removeTimeSeries() {
+ doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ assertThat(doubleGauge.getMetric(testClock))
+ .isEqualTo(
+ Metric.create(
+ METRIC_DESCRIPTOR,
+ Collections.singletonList(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(0), TEST_TIME), null))));
+
+ doubleGauge.removeTimeSeries(LABEL_VALUES);
+ assertThat(doubleGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void removeTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ doubleGauge.removeTimeSeries(null);
+ }
+
+ @Test
+ public void clear() {
+ DoublePoint doublePoint = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ doublePoint.add(-11);
+ DoublePoint defaultPoint = doubleGauge.getDefaultTimeSeries();
+ defaultPoint.set(100);
+
+ Metric metric = doubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(2);
+
+ doubleGauge.clear();
+ assertThat(doubleGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void setDefaultLabelValues() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ DoubleGaugeImpl doubleGauge =
+ new DoubleGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+ DoublePoint defaultPoint = doubleGauge.getDefaultTimeSeries();
+ defaultPoint.set(-230);
+
+ Metric metric = doubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().size()).isEqualTo(2);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().get(0)).isEqualTo(UNSET_VALUE);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().get(1)).isEqualTo(UNSET_VALUE);
+ }
+
+ @Test
+ public void pointImpl_InstanceOf() {
+ DoublePoint doublePoint = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ assertThat(doublePoint).isInstanceOf(DoubleGaugeImpl.PointImpl.class);
+ }
+
+ @Test
+ public void multipleMetrics_GetMetric() {
+ DoublePoint doublePoint = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ doublePoint.add(1);
+ doublePoint.add(2);
+
+ DoublePoint defaultPoint = doubleGauge.getDefaultTimeSeries();
+ defaultPoint.set(100);
+
+ DoublePoint doublePoint1 = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES1);
+ doublePoint1.add(-100);
+ doublePoint1.add(-20);
+
+ List<TimeSeries> expectedTimeSeriesList = new ArrayList<TimeSeries>();
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(3), TEST_TIME), null));
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ DEFAULT_LABEL_VALUES, Point.create(Value.doubleValue(100), TEST_TIME), null));
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES1, Point.create(Value.doubleValue(-120), TEST_TIME), null));
+
+ Metric metric = doubleGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(3);
+ assertThat(metric.getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeriesList);
+ }
+
+ @Test
+ public void empty_GetMetrics() {
+ assertThat(doubleGauge.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void testEquals() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ List<LabelValue> labelValues =
+ Arrays.asList(LabelValue.create("value1"), LabelValue.create("value2"));
+
+ DoubleGaugeImpl doubleGauge =
+ new DoubleGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+
+ DoublePoint defaultPoint1 = doubleGauge.getDefaultTimeSeries();
+ DoublePoint defaultPoint2 = doubleGauge.getDefaultTimeSeries();
+ DoublePoint doublePoint1 = doubleGauge.getOrCreateTimeSeries(labelValues);
+ DoublePoint doublePoint2 = doubleGauge.getOrCreateTimeSeries(labelValues);
+
+ new EqualsTester()
+ .addEqualityGroup(defaultPoint1, defaultPoint2)
+ .addEqualityGroup(doublePoint1, doublePoint2)
+ .testEquals();
+
+ doubleGauge.clear();
+
+ DoublePoint newDefaultPointAfterClear = doubleGauge.getDefaultTimeSeries();
+ DoublePoint newDoublePointAfterClear = doubleGauge.getOrCreateTimeSeries(labelValues);
+
+ doubleGauge.removeTimeSeries(labelValues);
+ DoublePoint newDoublePointAfterRemove = doubleGauge.getOrCreateTimeSeries(labelValues);
+
+ new EqualsTester()
+ .addEqualityGroup(defaultPoint1, defaultPoint2)
+ .addEqualityGroup(doublePoint1, doublePoint2)
+ .addEqualityGroup(newDefaultPointAfterClear)
+ .addEqualityGroup(newDoublePointAfterClear)
+ .addEqualityGroup(newDoublePointAfterRemove)
+ .testEquals();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/LongGaugeImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/LongGaugeImplTest.java
new file mode 100644
index 00000000..e83bb642
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/LongGaugeImplTest.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2018, 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.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.opencensus.implcore.metrics.LongGaugeImpl.UNSET_VALUE;
+
+import com.google.common.testing.EqualsTester;
+import io.opencensus.common.Timestamp;
+import io.opencensus.metrics.LabelKey;
+import io.opencensus.metrics.LabelValue;
+import io.opencensus.metrics.LongGauge.LongPoint;
+import io.opencensus.metrics.export.Metric;
+import io.opencensus.metrics.export.MetricDescriptor;
+import io.opencensus.metrics.export.MetricDescriptor.Type;
+import io.opencensus.metrics.export.Point;
+import io.opencensus.metrics.export.TimeSeries;
+import io.opencensus.metrics.export.Value;
+import io.opencensus.testing.common.TestClock;
+import java.util.ArrayList;
+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;
+
+/** Unit tests for {@link LongGaugeImpl}. */
+@RunWith(JUnit4.class)
+public class LongGaugeImplTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ private static final String METRIC_NAME = "name";
+ private static final String METRIC_DESCRIPTION = "description";
+ private static final String METRIC_UNIT = "1";
+ private static final List<LabelKey> LABEL_KEY =
+ Collections.singletonList(LabelKey.create("key", "key description"));
+ private static final List<LabelValue> LABEL_VALUES =
+ Collections.singletonList(LabelValue.create("value"));
+ private static final List<LabelValue> LABEL_VALUES1 =
+ Collections.singletonList(LabelValue.create("value1"));
+ private static final List<LabelValue> DEFAULT_LABEL_VALUES =
+ Collections.singletonList(UNSET_VALUE);
+
+ private static final Timestamp TEST_TIME = Timestamp.create(1234, 123);
+ private final TestClock testClock = TestClock.create(TEST_TIME);
+ private static final MetricDescriptor METRIC_DESCRIPTOR =
+ MetricDescriptor.create(
+ METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, Type.GAUGE_INT64, LABEL_KEY);
+ private final LongGaugeImpl longGaugeMetric =
+ new LongGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, LABEL_KEY);
+
+ @Test
+ public void getOrCreateTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ longGaugeMetric.getOrCreateTimeSeries(null);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries_WithNullElement() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ List<LabelValue> labelValues = Arrays.asList(LabelValue.create("value1"), null);
+
+ LongGaugeImpl longGauge =
+ new LongGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValue element should not be null.");
+ longGauge.getOrCreateTimeSeries(labelValues);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries_WithInvalidLabelSize() {
+ List<LabelValue> labelValues =
+ Arrays.asList(LabelValue.create("value1"), LabelValue.create("value2"));
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("Incorrect number of labels.");
+ longGaugeMetric.getOrCreateTimeSeries(labelValues);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries() {
+ LongPoint point = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ point.add(100);
+ LongPoint point1 = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ point1.set(500);
+
+ Metric metric = longGaugeMetric.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(500), TEST_TIME), null)));
+ assertThat(point).isSameAs(point1);
+ }
+
+ @Test
+ public void getOrCreateTimeSeries_WithNegativePointValues() {
+ LongPoint point = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ point.add(-100);
+ point.add(-33);
+
+ Metric metric = longGaugeMetric.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getPoints().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getPoints().get(0).getValue())
+ .isEqualTo(Value.longValue(-133));
+ assertThat(metric.getTimeSeriesList().get(0).getPoints().get(0).getTimestamp())
+ .isEqualTo(TEST_TIME);
+ assertThat(metric.getTimeSeriesList().get(0).getStartTimestamp()).isNull();
+ }
+
+ @Test
+ public void getDefaultTimeSeries() {
+ LongPoint point = longGaugeMetric.getDefaultTimeSeries();
+ point.add(100);
+ point.set(500);
+
+ LongPoint point1 = longGaugeMetric.getDefaultTimeSeries();
+ point1.add(-100);
+
+ Metric metric = longGaugeMetric.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric)
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ DEFAULT_LABEL_VALUES, Point.create(Value.longValue(400), TEST_TIME), null)));
+ assertThat(point).isSameAs(point1);
+ }
+
+ @Test
+ public void removeTimeSeries() {
+ longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ assertThat(longGaugeMetric.getMetric(testClock))
+ .isEqualTo(
+ Metric.createWithOneTimeSeries(
+ METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(0), TEST_TIME), null)));
+
+ longGaugeMetric.removeTimeSeries(LABEL_VALUES);
+ assertThat(longGaugeMetric.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void removeTimeSeries_WithNullLabelValues() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelValues");
+ longGaugeMetric.removeTimeSeries(null);
+ }
+
+ @Test
+ public void clear() {
+ LongPoint longPoint = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ longPoint.add(-11);
+ LongPoint defaultPoint = longGaugeMetric.getDefaultTimeSeries();
+ defaultPoint.set(100);
+
+ Metric metric = longGaugeMetric.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(2);
+
+ longGaugeMetric.clear();
+ assertThat(longGaugeMetric.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void setDefaultLabelValues() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ LongGaugeImpl longGauge =
+ new LongGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+ LongPoint defaultPoint = longGauge.getDefaultTimeSeries();
+ defaultPoint.set(-230);
+
+ Metric metric = longGauge.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(1);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().size()).isEqualTo(2);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().get(0)).isEqualTo(UNSET_VALUE);
+ assertThat(metric.getTimeSeriesList().get(0).getLabelValues().get(1)).isEqualTo(UNSET_VALUE);
+ }
+
+ @Test
+ public void pointImpl_InstanceOf() {
+ LongPoint longPoint = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ assertThat(longPoint).isInstanceOf(LongGaugeImpl.PointImpl.class);
+ }
+
+ @Test
+ public void multipleMetrics_GetMetric() {
+ LongPoint longPoint = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES);
+ longPoint.add(1);
+ longPoint.add(2);
+
+ LongPoint defaultPoint = longGaugeMetric.getDefaultTimeSeries();
+ defaultPoint.set(100);
+
+ LongPoint longPoint1 = longGaugeMetric.getOrCreateTimeSeries(LABEL_VALUES1);
+ longPoint1.add(-100);
+ longPoint1.add(-20);
+
+ List<TimeSeries> expectedTimeSeriesList = new ArrayList<TimeSeries>();
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(3), TEST_TIME), null));
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ DEFAULT_LABEL_VALUES, Point.create(Value.longValue(100), TEST_TIME), null));
+ expectedTimeSeriesList.add(
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES1, Point.create(Value.longValue(-120), TEST_TIME), null));
+
+ Metric metric = longGaugeMetric.getMetric(testClock);
+ assertThat(metric).isNotNull();
+ assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR);
+ assertThat(metric.getTimeSeriesList().size()).isEqualTo(3);
+ assertThat(metric.getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeriesList);
+ }
+
+ @Test
+ public void empty_GetMetrics() {
+ assertThat(longGaugeMetric.getMetric(testClock)).isNull();
+ }
+
+ @Test
+ public void testEquals() {
+ List<LabelKey> labelKeys =
+ Arrays.asList(LabelKey.create("key1", "desc"), LabelKey.create("key2", "desc"));
+ List<LabelValue> labelValues =
+ Arrays.asList(LabelValue.create("value1"), LabelValue.create("value2"));
+
+ LongGaugeImpl longGauge =
+ new LongGaugeImpl(METRIC_NAME, METRIC_DESCRIPTION, METRIC_UNIT, labelKeys);
+
+ LongPoint defaultPoint1 = longGauge.getDefaultTimeSeries();
+ LongPoint defaultPoint2 = longGauge.getDefaultTimeSeries();
+ LongPoint longPoint1 = longGauge.getOrCreateTimeSeries(labelValues);
+ LongPoint longPoint2 = longGauge.getOrCreateTimeSeries(labelValues);
+
+ new EqualsTester()
+ .addEqualityGroup(defaultPoint1, defaultPoint2)
+ .addEqualityGroup(longPoint1, longPoint2)
+ .testEquals();
+
+ longGauge.clear();
+
+ LongPoint newDefaultPointAfterClear = longGauge.getDefaultTimeSeries();
+ LongPoint newLongPointAfterClear = longGauge.getOrCreateTimeSeries(labelValues);
+
+ longGauge.removeTimeSeries(labelValues);
+ LongPoint newLongPointAfterRemove = longGauge.getOrCreateTimeSeries(labelValues);
+
+ new EqualsTester()
+ .addEqualityGroup(defaultPoint1, defaultPoint2)
+ .addEqualityGroup(longPoint1, longPoint2)
+ .addEqualityGroup(newDefaultPointAfterClear)
+ .addEqualityGroup(newLongPointAfterClear)
+ .addEqualityGroup(newLongPointAfterRemove)
+ .testEquals();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java
new file mode 100644
index 00000000..68bfda31
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricRegistryImplTest.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright 2018, 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.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Timestamp;
+import io.opencensus.common.ToDoubleFunction;
+import io.opencensus.common.ToLongFunction;
+import io.opencensus.metrics.DerivedDoubleGauge;
+import io.opencensus.metrics.DerivedLongGauge;
+import io.opencensus.metrics.DoubleGauge;
+import io.opencensus.metrics.DoubleGauge.DoublePoint;
+import io.opencensus.metrics.LabelKey;
+import io.opencensus.metrics.LabelValue;
+import io.opencensus.metrics.LongGauge;
+import io.opencensus.metrics.LongGauge.LongPoint;
+import io.opencensus.metrics.export.Metric;
+import io.opencensus.metrics.export.MetricDescriptor;
+import io.opencensus.metrics.export.MetricDescriptor.Type;
+import io.opencensus.metrics.export.Point;
+import io.opencensus.metrics.export.TimeSeries;
+import io.opencensus.metrics.export.Value;
+import io.opencensus.testing.common.TestClock;
+import java.util.Collection;
+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;
+
+/** Unit tests for {@link MetricRegistryImpl}. */
+@RunWith(JUnit4.class)
+public class MetricRegistryImplTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ private static final String NAME = "name";
+ private static final String NAME_2 = "name2";
+ private static final String NAME_3 = "name3";
+ private static final String NAME_4 = "name4";
+ private static final String DESCRIPTION = "description";
+ private static final String UNIT = "1";
+ private static final List<LabelKey> LABEL_KEY =
+ Collections.singletonList(LabelKey.create("key", "key description"));
+ private static final List<LabelValue> LABEL_VALUES =
+ Collections.singletonList(LabelValue.create("value"));
+
+ private static final Timestamp TEST_TIME = Timestamp.create(1234, 123);
+ private final TestClock testClock = TestClock.create(TEST_TIME);
+ private final MetricRegistryImpl metricRegistry = new MetricRegistryImpl(testClock);
+
+ private static final MetricDescriptor LONG_METRIC_DESCRIPTOR =
+ MetricDescriptor.create(NAME, DESCRIPTION, UNIT, Type.GAUGE_INT64, LABEL_KEY);
+ private static final MetricDescriptor DOUBLE_METRIC_DESCRIPTOR =
+ MetricDescriptor.create(NAME_2, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, LABEL_KEY);
+ private static final MetricDescriptor DERIVED_LONG_METRIC_DESCRIPTOR =
+ MetricDescriptor.create(NAME_3, DESCRIPTION, UNIT, Type.GAUGE_INT64, LABEL_KEY);
+ private static final MetricDescriptor DERIVED_DOUBLE_METRIC_DESCRIPTOR =
+ MetricDescriptor.create(NAME_4, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, LABEL_KEY);
+
+ private static final ToLongFunction<Object> longFunction =
+ new ToLongFunction<Object>() {
+ @Override
+ public long applyAsLong(Object value) {
+ return 5;
+ }
+ };
+ private static final ToDoubleFunction<Object> doubleFunction =
+ new ToDoubleFunction<Object>() {
+ @Override
+ public double applyAsDouble(Object value) {
+ return 5.0;
+ }
+ };
+
+ @Test
+ public void addLongGauge_NullName() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("name");
+ metricRegistry.addLongGauge(null, DESCRIPTION, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addLongGauge_NullDescription() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("description");
+ metricRegistry.addLongGauge(NAME, null, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addLongGauge_NullUnit() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("unit");
+ metricRegistry.addLongGauge(NAME, DESCRIPTION, null, LABEL_KEY);
+ }
+
+ @Test
+ public void addLongGauge_NullLabels() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKeys");
+ metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, null);
+ }
+
+ @Test
+ public void addLongGauge_WithNullElement() {
+ List<LabelKey> labelKeys = Collections.singletonList(null);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKey element should not be null.");
+ metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, labelKeys);
+ }
+
+ @Test
+ public void addDoubleGauge_NullName() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("name");
+ metricRegistry.addDoubleGauge(null, DESCRIPTION, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addDoubleGauge_NullDescription() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("description");
+ metricRegistry.addDoubleGauge(NAME_2, null, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addDoubleGauge_NullUnit() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("unit");
+ metricRegistry.addDoubleGauge(NAME_2, DESCRIPTION, null, LABEL_KEY);
+ }
+
+ @Test
+ public void addDoubleGauge_NullLabels() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKeys");
+ metricRegistry.addDoubleGauge(NAME_2, DESCRIPTION, UNIT, null);
+ }
+
+ @Test
+ public void addDoubleGauge_WithNullElement() {
+ List<LabelKey> labelKeys = Collections.singletonList(null);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKey element should not be null.");
+ metricRegistry.addDoubleGauge(NAME_2, DESCRIPTION, UNIT, labelKeys);
+ }
+
+ @Test
+ public void addDerivedLongGauge_NullName() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("name");
+ metricRegistry.addDerivedLongGauge(null, DESCRIPTION, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addDerivedLongGauge_NullDescription() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("description");
+ metricRegistry.addDerivedLongGauge(NAME_3, null, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addDerivedLongGauge_NullUnit() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("unit");
+ metricRegistry.addDerivedLongGauge(NAME_3, DESCRIPTION, null, LABEL_KEY);
+ }
+
+ @Test
+ public void addDerivedLongGauge_NullLabels() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKeys");
+ metricRegistry.addDerivedLongGauge(NAME_3, DESCRIPTION, UNIT, null);
+ }
+
+ @Test
+ public void addDerivedLongGauge_WithNullElement() {
+ List<LabelKey> labelKeys = Collections.singletonList(null);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKey element should not be null.");
+ metricRegistry.addDerivedLongGauge(NAME_3, DESCRIPTION, UNIT, labelKeys);
+ }
+
+ @Test
+ public void addDerivedDoubleGauge_NullName() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("name");
+ metricRegistry.addDerivedDoubleGauge(null, DESCRIPTION, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addDerivedDoubleGauge_NullDescription() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("description");
+ metricRegistry.addDerivedDoubleGauge(NAME_4, null, UNIT, LABEL_KEY);
+ }
+
+ @Test
+ public void addDerivedDoubleGauge_NullUnit() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("unit");
+ metricRegistry.addDerivedDoubleGauge(NAME_4, DESCRIPTION, null, LABEL_KEY);
+ }
+
+ @Test
+ public void addDerivedDoubleGauge_NullLabels() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKeys");
+ metricRegistry.addDerivedDoubleGauge(NAME_4, DESCRIPTION, UNIT, null);
+ }
+
+ @Test
+ public void addDerivedDoubleGauge_WithNullElement() {
+ List<LabelKey> labelKeys = Collections.singletonList(null);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("labelKey element should not be null.");
+ metricRegistry.addDerivedDoubleGauge(NAME_4, DESCRIPTION, UNIT, labelKeys);
+ }
+
+ @Test
+ public void addLongGauge_GetMetrics() {
+ LongGauge longGauge = metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, LABEL_KEY);
+ longGauge.getOrCreateTimeSeries(LABEL_VALUES);
+
+ Collection<Metric> metricCollections = metricRegistry.getMetricProducer().getMetrics();
+ assertThat(metricCollections.size()).isEqualTo(1);
+ assertThat(metricCollections)
+ .containsExactly(
+ Metric.createWithOneTimeSeries(
+ LONG_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(0), TEST_TIME), null)));
+ }
+
+ @Test
+ public void addDoubleGauge_GetMetrics() {
+ DoubleGauge doubleGauge = metricRegistry.addDoubleGauge(NAME_2, DESCRIPTION, UNIT, LABEL_KEY);
+ doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ Collection<Metric> metricCollections = metricRegistry.getMetricProducer().getMetrics();
+ assertThat(metricCollections.size()).isEqualTo(1);
+ assertThat(metricCollections)
+ .containsExactly(
+ Metric.createWithOneTimeSeries(
+ DOUBLE_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(0.0), TEST_TIME), null)));
+ }
+
+ @Test
+ public void addDerivedLongGauge_GetMetrics() {
+ DerivedLongGauge derivedLongGauge =
+ metricRegistry.addDerivedLongGauge(NAME_3, DESCRIPTION, UNIT, LABEL_KEY);
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, longFunction);
+ Collection<Metric> metricCollections = metricRegistry.getMetricProducer().getMetrics();
+ assertThat(metricCollections.size()).isEqualTo(1);
+ assertThat(metricCollections)
+ .containsExactly(
+ Metric.createWithOneTimeSeries(
+ DERIVED_LONG_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(5), TEST_TIME), null)));
+ }
+
+ @Test
+ public void addDerivedDoubleGauge_GetMetrics() {
+ DerivedDoubleGauge derivedDoubleGauge =
+ metricRegistry.addDerivedDoubleGauge(NAME_4, DESCRIPTION, UNIT, LABEL_KEY);
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, doubleFunction);
+ Collection<Metric> metricCollections = metricRegistry.getMetricProducer().getMetrics();
+ assertThat(metricCollections.size()).isEqualTo(1);
+ assertThat(metricCollections)
+ .containsExactly(
+ Metric.createWithOneTimeSeries(
+ DERIVED_DOUBLE_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(5.0), TEST_TIME), null)));
+ }
+
+ @Test
+ public void empty_GetMetrics() {
+ assertThat(metricRegistry.getMetricProducer().getMetrics()).isEmpty();
+ }
+
+ @Test
+ public void checkInstanceOf() {
+ assertThat(metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, LABEL_KEY))
+ .isInstanceOf(LongGaugeImpl.class);
+ assertThat(metricRegistry.addDoubleGauge(NAME_2, DESCRIPTION, UNIT, LABEL_KEY))
+ .isInstanceOf(DoubleGaugeImpl.class);
+ assertThat(metricRegistry.addDerivedLongGauge(NAME_3, DESCRIPTION, UNIT, LABEL_KEY))
+ .isInstanceOf(DerivedLongGaugeImpl.class);
+ assertThat(metricRegistry.addDerivedDoubleGauge(NAME_4, DESCRIPTION, UNIT, LABEL_KEY))
+ .isInstanceOf(DerivedDoubleGaugeImpl.class);
+ }
+
+ @Test
+ public void getMetrics() {
+ LongGauge longGauge = metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, LABEL_KEY);
+ LongPoint longPoint = longGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ longPoint.set(200);
+ DoubleGauge doubleGauge = metricRegistry.addDoubleGauge(NAME_2, DESCRIPTION, UNIT, LABEL_KEY);
+ DoublePoint doublePoint = doubleGauge.getOrCreateTimeSeries(LABEL_VALUES);
+ doublePoint.set(-300.13);
+ DerivedLongGauge derivedLongGauge =
+ metricRegistry.addDerivedLongGauge(NAME_3, DESCRIPTION, UNIT, LABEL_KEY);
+ derivedLongGauge.createTimeSeries(LABEL_VALUES, null, longFunction);
+ DerivedDoubleGauge derivedDoubleGauge =
+ metricRegistry.addDerivedDoubleGauge(NAME_4, DESCRIPTION, UNIT, LABEL_KEY);
+ derivedDoubleGauge.createTimeSeries(LABEL_VALUES, null, doubleFunction);
+
+ Collection<Metric> metricCollections = metricRegistry.getMetricProducer().getMetrics();
+ assertThat(metricCollections.size()).isEqualTo(4);
+ assertThat(metricCollections)
+ .containsExactly(
+ Metric.createWithOneTimeSeries(
+ LONG_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(200), TEST_TIME), null)),
+ Metric.createWithOneTimeSeries(
+ DOUBLE_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(-300.13), TEST_TIME), null)),
+ Metric.createWithOneTimeSeries(
+ DERIVED_LONG_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.longValue(5), TEST_TIME), null)),
+ Metric.createWithOneTimeSeries(
+ DERIVED_DOUBLE_METRIC_DESCRIPTOR,
+ TimeSeries.createWithOnePoint(
+ LABEL_VALUES, Point.create(Value.doubleValue(5.0), TEST_TIME), null)));
+ }
+
+ @Test
+ public void registerDifferentMetricSameName() {
+ metricRegistry.addLongGauge(NAME, DESCRIPTION, UNIT, LABEL_KEY);
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("A different metric with the same name already registered.");
+ metricRegistry.addDoubleGauge(NAME, DESCRIPTION, UNIT, LABEL_KEY);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricsComponentImplBaseTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricsComponentImplBaseTest.java
new file mode 100644
index 00000000..7f8515d3
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/MetricsComponentImplBaseTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018, 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.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.implcore.metrics.export.ExportComponentImpl;
+import io.opencensus.testing.common.TestClock;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link MetricsComponentImplBase}. */
+@RunWith(JUnit4.class)
+public class MetricsComponentImplBaseTest {
+ private final MetricsComponentImplBase metricsComponentImplBase =
+ new MetricsComponentImplBase(TestClock.create());
+
+ @Test
+ public void getExportComponent() {
+ assertThat(metricsComponentImplBase.getExportComponent())
+ .isInstanceOf(ExportComponentImpl.class);
+ }
+
+ @Test
+ public void getMetricRegistry() {
+ assertThat(metricsComponentImplBase.getMetricRegistry()).isInstanceOf(MetricRegistryImpl.class);
+ }
+
+ @Test
+ public void metricRegistry_InstalledToMetricProducerManger() {
+ assertThat(
+ metricsComponentImplBase
+ .getExportComponent()
+ .getMetricProducerManager()
+ .getAllMetricProducer())
+ .containsExactly(metricsComponentImplBase.getMetricRegistry().getMetricProducer());
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/export/ExportComponentImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/export/ExportComponentImplTest.java
new file mode 100644
index 00000000..fb91641c
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/export/ExportComponentImplTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018, 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.metrics.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link ExportComponentImpl}. */
+@RunWith(JUnit4.class)
+public class ExportComponentImplTest {
+
+ @Test
+ public void getMetricProducerManager() {
+ ExportComponentImpl exportComponent = new ExportComponentImpl();
+ assertThat(exportComponent.getMetricProducerManager())
+ .isInstanceOf(MetricProducerManagerImpl.class);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/metrics/export/MetricProducerManagerImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/metrics/export/MetricProducerManagerImplTest.java
new file mode 100644
index 00000000..e549dadb
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/metrics/export/MetricProducerManagerImplTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2018, 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.metrics.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.metrics.export.MetricProducer;
+import io.opencensus.metrics.export.MetricProducerManager;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link MetricProducerManagerImpl}. */
+@RunWith(JUnit4.class)
+public class MetricProducerManagerImplTest {
+
+ private final MetricProducerManager metricProducerManager = new MetricProducerManagerImpl();
+ @Mock private MetricProducer metricProducer;
+ @Mock private MetricProducer metricProducerOther;
+
+ @Rule public final ExpectedException thrown = ExpectedException.none();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void add_DisallowsNull() {
+ thrown.expect(NullPointerException.class);
+ metricProducerManager.add(null);
+ }
+
+ @Test
+ public void add() {
+ metricProducerManager.add(metricProducer);
+ assertThat(metricProducerManager.getAllMetricProducer()).containsExactly(metricProducer);
+ }
+
+ @Test
+ public void add_DuplicateElement() {
+ metricProducerManager.add(metricProducer);
+ Set<MetricProducer> metricProducerSet = metricProducerManager.getAllMetricProducer();
+ assertThat(metricProducerSet).containsExactly(metricProducer);
+ metricProducerManager.add(metricProducer);
+ // Returns the same object.
+ assertThat(metricProducerManager.getAllMetricProducer()).isSameAs(metricProducerSet);
+ }
+
+ @Test
+ public void add_MultipleElements() {
+ metricProducerManager.add(metricProducer);
+ Set<MetricProducer> metricProducerSet = metricProducerManager.getAllMetricProducer();
+ assertThat(metricProducerSet).containsExactly(metricProducer);
+ metricProducerManager.add(metricProducerOther);
+ // Returns the same object.
+ assertThat(metricProducerManager.getAllMetricProducer())
+ .containsExactly(metricProducer, metricProducerOther);
+ }
+
+ @Test
+ public void addAndRemove() {
+ metricProducerManager.add(metricProducer);
+ assertThat(metricProducerManager.getAllMetricProducer()).containsExactly(metricProducer);
+ metricProducerManager.remove(metricProducer);
+ assertThat(metricProducerManager.getAllMetricProducer()).isEmpty();
+ }
+
+ @Test
+ public void remove_DisallowsNull() {
+ thrown.expect(NullPointerException.class);
+ metricProducerManager.remove(null);
+ }
+
+ @Test
+ public void remove_FromEmpty() {
+ metricProducerManager.remove(metricProducer);
+ assertThat(metricProducerManager.getAllMetricProducer()).isEmpty();
+ }
+
+ @Test
+ public void remove_NotPresent() {
+ metricProducerManager.add(metricProducer);
+ Set<MetricProducer> metricProducerSet = metricProducerManager.getAllMetricProducer();
+ assertThat(metricProducerSet).containsExactly(metricProducer);
+ metricProducerManager.remove(metricProducerOther);
+ // Returns the same object.
+ assertThat(metricProducerManager.getAllMetricProducer()).isSameAs(metricProducerSet);
+ }
+
+ @Test
+ public void getAllMetricProducer_empty() {
+ assertThat(metricProducerManager.getAllMetricProducer()).isEmpty();
+ }
+}
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..39a53e1a
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/IntervalBucketTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.stats.Measure.MeasureDouble;
+import io.opencensus.tags.TagValue;
+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 IntervalBucket}. */
+@RunWith(JUnit4.class)
+public class IntervalBucketTest {
+
+ @Rule public final ExpectedException thrown = ExpectedException.none();
+
+ private static final double TOLERANCE = 1e-6;
+ private static final MeasureDouble MEASURE_DOUBLE =
+ MeasureDouble.create("measure1", "description", "1");
+ 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, MEASURE_DOUBLE);
+ }
+
+ @Test
+ public void preventNullDuration() {
+ thrown.expect(NullPointerException.class);
+ new IntervalBucket(START, null, MEAN, MEASURE_DOUBLE);
+ }
+
+ @Test
+ public void preventNegativeDuration() {
+ thrown.expect(IllegalArgumentException.class);
+ new IntervalBucket(START, NEGATIVE_TEN_SEC, MEAN, MEASURE_DOUBLE);
+ }
+
+ @Test
+ public void preventNullAggregation() {
+ thrown.expect(NullPointerException.class);
+ new IntervalBucket(START, MINUTE, null, MEASURE_DOUBLE);
+ }
+
+ @Test
+ public void preventNullMeasure() {
+ thrown.expect(NullPointerException.class);
+ new IntervalBucket(START, MINUTE, MEAN, null);
+ }
+
+ @Test
+ public void testGetTagValueAggregationMap_empty() {
+ assertThat(new IntervalBucket(START, MINUTE, MEAN, MEASURE_DOUBLE).getTagValueAggregationMap())
+ .isEmpty();
+ }
+
+ @Test
+ public void testGetStart() {
+ assertThat(new IntervalBucket(START, MINUTE, MEAN, MEASURE_DOUBLE).getStart()).isEqualTo(START);
+ }
+
+ @Test
+ public void testRecord() {
+ IntervalBucket bucket = new IntervalBucket(START, MINUTE, MEAN, MEASURE_DOUBLE);
+ List<TagValue> tagValues1 = Arrays.<TagValue>asList(TagValue.create("VALUE1"));
+ List<TagValue> tagValues2 = Arrays.<TagValue>asList(TagValue.create("VALUE2"));
+ bucket.record(tagValues1, 5.0, Collections.<String, String>emptyMap(), START);
+ bucket.record(tagValues1, 15.0, Collections.<String, String>emptyMap(), START);
+ bucket.record(tagValues2, 10.0, Collections.<String, String>emptyMap(), START);
+ 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, MEASURE_DOUBLE)
+ .getFraction(thirtySecondsAfterStart))
+ .isWithin(TOLERANCE)
+ .of(0.5);
+ }
+
+ @Test
+ public void preventCallingGetFractionOnPastBuckets() {
+ IntervalBucket bucket = new IntervalBucket(START, MINUTE, MEAN, MEASURE_DOUBLE);
+ Timestamp twoMinutesAfterStart = Timestamp.create(180, 0);
+ thrown.expect(IllegalArgumentException.class);
+ bucket.getFraction(twoMinutesAfterStart);
+ }
+
+ @Test
+ public void preventCallingGetFractionOnFutureBuckets() {
+ IntervalBucket bucket = new IntervalBucket(START, MINUTE, MEAN, MEASURE_DOUBLE);
+ 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..19e8a6c5
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MeasureMapInternalTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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 testPutAttachment() {
+ MeasureMapInternal metrics =
+ MeasureMapInternal.builder()
+ .putAttachment("k1", "v1")
+ .putAttachment("k2", "v2")
+ .putAttachment("k1", "v3")
+ .build();
+ assertThat(metrics.getAttachments()).containsExactly("k1", "v3", "k2", "v2");
+ assertContains(metrics);
+ }
+
+ @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..25f33a94
--- /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.implcore.internal.CurrentState.State;
+import io.opencensus.stats.Aggregation.Mean;
+import io.opencensus.stats.Measure;
+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, State.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/MetricUtilsTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/MetricUtilsTest.java
new file mode 100644
index 00000000..66e971f6
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MetricUtilsTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2018, 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.metrics.LabelKey;
+import io.opencensus.metrics.LabelValue;
+import io.opencensus.metrics.export.MetricDescriptor;
+import io.opencensus.metrics.export.MetricDescriptor.Type;
+import io.opencensus.stats.Aggregation.Count;
+import io.opencensus.stats.Aggregation.Distribution;
+import io.opencensus.stats.Aggregation.LastValue;
+import io.opencensus.stats.Aggregation.Mean;
+import io.opencensus.stats.Aggregation.Sum;
+import io.opencensus.stats.BucketBoundaries;
+import io.opencensus.stats.Measure.MeasureDouble;
+import io.opencensus.stats.Measure.MeasureLong;
+import io.opencensus.stats.View;
+import io.opencensus.stats.View.AggregationWindow.Interval;
+import io.opencensus.stats.View.Name;
+import io.opencensus.tags.TagKey;
+import io.opencensus.tags.TagValue;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link MetricUtils}. */
+@RunWith(JUnit4.class)
+public class MetricUtilsTest {
+
+ 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 Duration TEN_SECONDS = Duration.create(10, 0);
+ private static final Interval INTERVAL = Interval.create(TEN_SECONDS);
+ private static final BucketBoundaries BUCKET_BOUNDARIES =
+ BucketBoundaries.create(Arrays.asList(-10.0, 0.0, 10.0));
+ private static final Sum SUM = Sum.create();
+ private static final Count COUNT = Count.create();
+ private static final Mean MEAN = Mean.create();
+ private static final Distribution DISTRIBUTION = Distribution.create(BUCKET_BOUNDARIES);
+ private static final LastValue LAST_VALUE = LastValue.create();
+ private static final View VIEW_1 =
+ View.create(
+ VIEW_NAME, VIEW_DESCRIPTION, MEASURE_DOUBLE, LAST_VALUE, Collections.singletonList(KEY));
+ private static final View VIEW_2 =
+ View.create(
+ VIEW_NAME_2,
+ VIEW_DESCRIPTION,
+ MEASURE_DOUBLE,
+ MEAN,
+ Collections.singletonList(KEY),
+ INTERVAL);
+ private static final Timestamp TIMESTAMP = Timestamp.fromMillis(1000);
+
+ @Test
+ public void viewToMetricDescriptor() {
+ MetricDescriptor metricDescriptor = MetricUtils.viewToMetricDescriptor(VIEW_1);
+ assertThat(metricDescriptor).isNotNull();
+ assertThat(metricDescriptor.getName()).isEqualTo(VIEW_NAME.asString());
+ assertThat(metricDescriptor.getUnit()).isEqualTo(MEASURE_UNIT);
+ assertThat(metricDescriptor.getType()).isEqualTo(Type.GAUGE_DOUBLE);
+ assertThat(metricDescriptor.getDescription()).isEqualTo(VIEW_DESCRIPTION);
+ assertThat(metricDescriptor.getLabelKeys()).containsExactly(LabelKey.create(KEY.getName(), ""));
+ }
+
+ @Test
+ public void viewToMetricDescriptor_NoIntervalViews() {
+ MetricDescriptor metricDescriptor = MetricUtils.viewToMetricDescriptor(VIEW_2);
+ assertThat(metricDescriptor).isNull();
+ }
+
+ @Test
+ public void getType() {
+ assertThat(MetricUtils.getType(MEASURE_DOUBLE, LAST_VALUE)).isEqualTo(Type.GAUGE_DOUBLE);
+ assertThat(MetricUtils.getType(MEASURE_LONG, LAST_VALUE)).isEqualTo(Type.GAUGE_INT64);
+ assertThat(MetricUtils.getType(MEASURE_DOUBLE, SUM)).isEqualTo(Type.CUMULATIVE_DOUBLE);
+ assertThat(MetricUtils.getType(MEASURE_LONG, SUM)).isEqualTo(Type.CUMULATIVE_INT64);
+ assertThat(MetricUtils.getType(MEASURE_DOUBLE, MEAN)).isEqualTo(Type.CUMULATIVE_DOUBLE);
+ assertThat(MetricUtils.getType(MEASURE_LONG, MEAN)).isEqualTo(Type.CUMULATIVE_DOUBLE);
+ assertThat(MetricUtils.getType(MEASURE_DOUBLE, COUNT)).isEqualTo(Type.CUMULATIVE_INT64);
+ assertThat(MetricUtils.getType(MEASURE_LONG, COUNT)).isEqualTo(Type.CUMULATIVE_INT64);
+ assertThat(MetricUtils.getType(MEASURE_DOUBLE, DISTRIBUTION))
+ .isEqualTo(Type.CUMULATIVE_DISTRIBUTION);
+ assertThat(MetricUtils.getType(MEASURE_LONG, DISTRIBUTION))
+ .isEqualTo(Type.CUMULATIVE_DISTRIBUTION);
+ }
+
+ @Test
+ public void tagValuesToLabelValues() {
+ List<TagValue> tagValues = Arrays.asList(VALUE, VALUE_2, null);
+ assertThat(MetricUtils.tagValuesToLabelValues(tagValues))
+ .containsExactly(
+ LabelValue.create(VALUE.asString()),
+ LabelValue.create(VALUE_2.asString()),
+ LabelValue.create(null));
+ }
+}
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..a6139e53
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MutableAggregationTest.java
@@ -0,0 +1,339 @@
+/*
+ * 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.assertAggregationDataEquals;
+
+import com.google.common.collect.ImmutableList;
+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.MutableLastValueDouble;
+import io.opencensus.implcore.stats.MutableAggregation.MutableLastValueLong;
+import io.opencensus.implcore.stats.MutableAggregation.MutableMean;
+import io.opencensus.implcore.stats.MutableAggregation.MutableSumDouble;
+import io.opencensus.implcore.stats.MutableAggregation.MutableSumLong;
+import io.opencensus.metrics.export.Distribution;
+import io.opencensus.metrics.export.Distribution.Bucket;
+import io.opencensus.metrics.export.Distribution.BucketOptions;
+import io.opencensus.metrics.export.Point;
+import io.opencensus.metrics.export.Value;
+import io.opencensus.stats.AggregationData;
+import io.opencensus.stats.AggregationData.CountData;
+import io.opencensus.stats.AggregationData.DistributionData;
+import io.opencensus.stats.AggregationData.DistributionData.Exemplar;
+import io.opencensus.stats.AggregationData.LastValueDataDouble;
+import io.opencensus.stats.AggregationData.LastValueDataLong;
+import io.opencensus.stats.AggregationData.MeanData;
+import io.opencensus.stats.AggregationData.SumDataDouble;
+import io.opencensus.stats.AggregationData.SumDataLong;
+import io.opencensus.stats.BucketBoundaries;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+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));
+ private static final BucketBoundaries BUCKET_BOUNDARIES_EMPTY =
+ BucketBoundaries.create(Collections.<Double>emptyList());
+ private static final Timestamp TIMESTAMP = Timestamp.create(60, 0);
+
+ @Test
+ public void testCreateEmpty() {
+ assertThat(MutableSumDouble.create().getSum()).isWithin(TOLERANCE).of(0);
+ assertThat(MutableSumLong.create().getSum()).isWithin(TOLERANCE).of(0);
+ assertThat(MutableCount.create().getCount()).isEqualTo(0);
+ assertThat(MutableMean.create().getMean()).isWithin(TOLERANCE).of(0);
+ assertThat(MutableLastValueDouble.create().getLastValue()).isNaN();
+ assertThat(MutableLastValueLong.create().getLastValue()).isNaN();
+
+ 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]);
+ assertThat(mutableDistribution.getExemplars()).isEqualTo(new Exemplar[4]);
+
+ MutableDistribution mutableDistributionNoHistogram =
+ MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY);
+ assertThat(mutableDistributionNoHistogram.getExemplars()).isNull();
+ }
+
+ @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(
+ MutableSumDouble.create(),
+ MutableSumLong.create(),
+ MutableCount.create(),
+ MutableMean.create(),
+ MutableDistribution.create(BUCKET_BOUNDARIES),
+ MutableLastValueDouble.create(),
+ MutableLastValueLong.create());
+
+ 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, Collections.<String, String>emptyMap(), TIMESTAMP);
+ }
+ }
+
+ assertAggregationDataEquals(
+ aggregations.get(0).toAggregationData(),
+ AggregationData.SumDataDouble.create(20.0),
+ TOLERANCE);
+ assertAggregationDataEquals(
+ aggregations.get(1).toAggregationData(), AggregationData.SumDataLong.create(20), TOLERANCE);
+ assertAggregationDataEquals(
+ aggregations.get(2).toAggregationData(), AggregationData.CountData.create(5), TOLERANCE);
+ assertAggregationDataEquals(
+ aggregations.get(3).toAggregationData(),
+ AggregationData.MeanData.create(4.0, 5),
+ TOLERANCE);
+ assertAggregationDataEquals(
+ aggregations.get(4).toAggregationData(),
+ AggregationData.DistributionData.create(
+ 4.0, 5, -5.0, 20.0, 372, Arrays.asList(0L, 2L, 2L, 1L)),
+ TOLERANCE);
+ assertAggregationDataEquals(
+ aggregations.get(5).toAggregationData(),
+ AggregationData.LastValueDataDouble.create(5.0),
+ TOLERANCE);
+ assertAggregationDataEquals(
+ aggregations.get(6).toAggregationData(),
+ AggregationData.LastValueDataLong.create(5),
+ TOLERANCE);
+ }
+
+ @Test
+ public void testAdd_DistributionWithExemplarAttachments() {
+ MutableDistribution mutableDistribution = MutableDistribution.create(BUCKET_BOUNDARIES);
+ MutableDistribution mutableDistributionNoHistogram =
+ MutableDistribution.create(BUCKET_BOUNDARIES_EMPTY);
+ List<Double> values = Arrays.asList(-1.0, 1.0, -5.0, 20.0, 5.0);
+ List<Map<String, String>> attachmentsList =
+ ImmutableList.<Map<String, String>>of(
+ Collections.<String, String>singletonMap("k1", "v1"),
+ Collections.<String, String>singletonMap("k2", "v2"),
+ Collections.<String, String>singletonMap("k3", "v3"),
+ Collections.<String, String>singletonMap("k4", "v4"),
+ Collections.<String, String>singletonMap("k5", "v5"));
+ List<Timestamp> timestamps =
+ Arrays.asList(
+ Timestamp.fromMillis(500),
+ Timestamp.fromMillis(1000),
+ Timestamp.fromMillis(2000),
+ Timestamp.fromMillis(3000),
+ Timestamp.fromMillis(4000));
+ for (int i = 0; i < values.size(); i++) {
+ mutableDistribution.add(values.get(i), attachmentsList.get(i), timestamps.get(i));
+ mutableDistributionNoHistogram.add(values.get(i), attachmentsList.get(i), timestamps.get(i));
+ }
+
+ // Each bucket can only have up to one exemplar. If there are more than one exemplars in a
+ // bucket, only the last one will be kept.
+ List<Exemplar> expected =
+ Arrays.<Exemplar>asList(
+ null,
+ Exemplar.create(values.get(2), timestamps.get(2), attachmentsList.get(2)),
+ Exemplar.create(values.get(4), timestamps.get(4), attachmentsList.get(4)),
+ Exemplar.create(values.get(3), timestamps.get(3), attachmentsList.get(3)));
+ assertThat(mutableDistribution.getExemplars())
+ .asList()
+ .containsExactlyElementsIn(expected)
+ .inOrder();
+ assertThat(mutableDistributionNoHistogram.getExemplars()).isNull();
+ }
+
+ @Test
+ public void testCombine_SumCountMean() {
+ // combine() for Mutable Sum, Count and Mean will pick up fractional stats
+ List<MutableAggregation> aggregations1 =
+ Arrays.asList(
+ MutableSumDouble.create(),
+ MutableSumLong.create(),
+ MutableCount.create(),
+ MutableMean.create());
+ List<MutableAggregation> aggregations2 =
+ Arrays.asList(
+ MutableSumDouble.create(),
+ MutableSumLong.create(),
+ MutableCount.create(),
+ MutableMean.create());
+
+ for (double val : Arrays.asList(-1.0, -5.0)) {
+ for (MutableAggregation aggregation : aggregations1) {
+ aggregation.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
+ }
+ }
+ for (double val : Arrays.asList(10.0, 50.0)) {
+ for (MutableAggregation aggregation : aggregations2) {
+ aggregation.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
+ }
+ }
+
+ List<MutableAggregation> combined =
+ Arrays.asList(
+ MutableSumDouble.create(),
+ MutableSumLong.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(((MutableSumDouble) combined.get(0)).getSum()).isWithin(TOLERANCE).of(30);
+ assertThat(((MutableSumLong) combined.get(1)).getSum()).isWithin(TOLERANCE).of(30);
+ assertThat(((MutableCount) combined.get(2)).getCount()).isEqualTo(3);
+ assertThat(((MutableMean) combined.get(3)).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, Collections.<String, String>emptyMap(), TIMESTAMP);
+ }
+ for (double val : Arrays.asList(10.0, 20.0)) {
+ distribution2.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
+ }
+ for (double val : Arrays.asList(-10.0, 15.0, -15.0, -20.0)) {
+ distribution3.add(val, Collections.<String, String>emptyMap(), TIMESTAMP);
+ }
+
+ 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);
+ }
+
+ @Test
+ public void mutableAggregation_ToAggregationData() {
+ assertThat(MutableSumDouble.create().toAggregationData()).isEqualTo(SumDataDouble.create(0));
+ assertThat(MutableSumLong.create().toAggregationData()).isEqualTo(SumDataLong.create(0));
+ assertThat(MutableCount.create().toAggregationData()).isEqualTo(CountData.create(0));
+ assertThat(MutableMean.create().toAggregationData()).isEqualTo(MeanData.create(0, 0));
+ assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toAggregationData())
+ .isEqualTo(
+ DistributionData.create(
+ 0,
+ 0,
+ Double.POSITIVE_INFINITY,
+ Double.NEGATIVE_INFINITY,
+ 0,
+ Arrays.asList(0L, 0L, 0L, 0L)));
+ assertThat(MutableLastValueDouble.create().toAggregationData())
+ .isEqualTo(LastValueDataDouble.create(Double.NaN));
+ assertThat(MutableLastValueLong.create().toAggregationData())
+ .isEqualTo(LastValueDataLong.create(0));
+ }
+
+ @Test
+ public void mutableAggregation_ToPoint() {
+ assertThat(MutableSumDouble.create().toPoint(TIMESTAMP))
+ .isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP));
+ assertThat(MutableSumLong.create().toPoint(TIMESTAMP))
+ .isEqualTo(Point.create(Value.longValue(0), TIMESTAMP));
+ assertThat(MutableCount.create().toPoint(TIMESTAMP))
+ .isEqualTo(Point.create(Value.longValue(0), TIMESTAMP));
+ assertThat(MutableMean.create().toPoint(TIMESTAMP))
+ .isEqualTo(Point.create(Value.doubleValue(0), TIMESTAMP));
+
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage("bucket boundary should be > 0");
+ assertThat(MutableDistribution.create(BUCKET_BOUNDARIES).toPoint(TIMESTAMP))
+ .isEqualTo(
+ Point.create(
+ Value.distributionValue(
+ Distribution.create(
+ 0,
+ 0,
+ 0,
+ BucketOptions.explicitOptions(BUCKET_BOUNDARIES.getBoundaries()),
+ Arrays.asList(
+ Bucket.create(0),
+ Bucket.create(0),
+ Bucket.create(0),
+ Bucket.create(0)))),
+ TIMESTAMP));
+ }
+
+ 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..06f50fed
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/MutableViewDataTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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 org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link MutableViewData}. */
+@RunWith(JUnit4.class)
+public class MutableViewDataTest {
+
+ @Test
+ public void testConstants() {
+ assertThat(MutableViewData.ZERO_TIMESTAMP).isEqualTo(Timestamp.create(0, 0));
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/stats/RecordUtilsTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/RecordUtilsTest.java
new file mode 100644
index 00000000..1e22a7a1
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/RecordUtilsTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2018, 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.ImmutableMap;
+import io.opencensus.implcore.stats.MutableAggregation.MutableDistribution;
+import io.opencensus.stats.Aggregation.Count;
+import io.opencensus.stats.Aggregation.Distribution;
+import io.opencensus.stats.Aggregation.LastValue;
+import io.opencensus.stats.Aggregation.Mean;
+import io.opencensus.stats.Aggregation.Sum;
+import io.opencensus.stats.AggregationData.CountData;
+import io.opencensus.stats.AggregationData.LastValueDataDouble;
+import io.opencensus.stats.AggregationData.LastValueDataLong;
+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.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 RecordUtils}. */
+@RunWith(JUnit4.class)
+public class RecordUtilsTest {
+
+ private static final double EPSILON = 1e-7;
+ private static final MeasureDouble MEASURE_DOUBLE =
+ MeasureDouble.create("measure1", "description", "1");
+ private static final MeasureLong MEASURE_LONG =
+ MeasureLong.create("measure2", "description", "1");
+ 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");
+
+ @Test
+ public void testConstants() {
+ assertThat(RecordUtils.UNKNOWN_TAG_VALUE).isNull();
+ }
+
+ @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(RecordUtils.getTagValues(tags, columns))
+ .containsExactly(CALLER_V, METHOD_V, RecordUtils.UNKNOWN_TAG_VALUE)
+ .inOrder();
+ }
+
+ @Test
+ public void createMutableAggregation() {
+ BucketBoundaries bucketBoundaries = BucketBoundaries.create(Arrays.asList(-1.0, 0.0, 1.0));
+
+ assertThat(
+ RecordUtils.createMutableAggregation(Sum.create(), MEASURE_DOUBLE).toAggregationData())
+ .isEqualTo(SumDataDouble.create(0));
+ assertThat(RecordUtils.createMutableAggregation(Sum.create(), MEASURE_LONG).toAggregationData())
+ .isEqualTo(SumDataLong.create(0));
+ assertThat(
+ RecordUtils.createMutableAggregation(Count.create(), MEASURE_DOUBLE)
+ .toAggregationData())
+ .isEqualTo(CountData.create(0));
+ assertThat(
+ RecordUtils.createMutableAggregation(Count.create(), MEASURE_LONG).toAggregationData())
+ .isEqualTo(CountData.create(0));
+ assertThat(
+ RecordUtils.createMutableAggregation(Mean.create(), MEASURE_DOUBLE).toAggregationData())
+ .isEqualTo(MeanData.create(0, 0));
+ assertThat(
+ RecordUtils.createMutableAggregation(Mean.create(), MEASURE_LONG).toAggregationData())
+ .isEqualTo(MeanData.create(0, 0));
+ assertThat(
+ RecordUtils.createMutableAggregation(LastValue.create(), MEASURE_DOUBLE)
+ .toAggregationData())
+ .isEqualTo(LastValueDataDouble.create(Double.NaN));
+ assertThat(
+ RecordUtils.createMutableAggregation(LastValue.create(), MEASURE_LONG)
+ .toAggregationData())
+ .isEqualTo(LastValueDataLong.create(0));
+
+ MutableDistribution mutableDistribution =
+ (MutableDistribution)
+ RecordUtils.createMutableAggregation(
+ Distribution.create(bucketBoundaries), MEASURE_DOUBLE);
+ assertThat(mutableDistribution.getMin()).isPositiveInfinity();
+ assertThat(mutableDistribution.getMax()).isNegativeInfinity();
+ assertThat(mutableDistribution.getSumOfSquaredDeviations()).isWithin(EPSILON).of(0);
+ assertThat(mutableDistribution.getBucketCounts()).isEqualTo(new long[4]);
+ }
+}
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..04861df9
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsComponentImplBaseTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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
+ @SuppressWarnings("deprecation")
+ public void setState_Disabled() {
+ statsComponent.setState(StatsCollectionState.DISABLED);
+ assertThat(statsComponent.getState()).isEqualTo(StatsCollectionState.DISABLED);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void setState_Enabled() {
+ statsComponent.setState(StatsCollectionState.DISABLED);
+ statsComponent.setState(StatsCollectionState.ENABLED);
+ assertThat(statsComponent.getState()).isEqualTo(StatsCollectionState.ENABLED);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void setState_DisallowsNull() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("newState");
+ statsComponent.setState(null);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ 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..bd8b5b88
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsRecorderImplTest.java
@@ -0,0 +1,349 @@
+/*
+ * 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 io.grpc.Context;
+import io.opencensus.common.Duration;
+import io.opencensus.common.Timestamp;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.stats.StatsTestUtil.SimpleTagContext;
+import io.opencensus.stats.Aggregation.Count;
+import io.opencensus.stats.Aggregation.Distribution;
+import io.opencensus.stats.Aggregation.Sum;
+import io.opencensus.stats.AggregationData.CountData;
+import io.opencensus.stats.AggregationData.DistributionData;
+import io.opencensus.stats.AggregationData.DistributionData.Exemplar;
+import io.opencensus.stats.BucketBoundaries;
+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.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 MeasureDouble MEASURE_DOUBLE_NO_VIEW_1 =
+ MeasureDouble.create("my measurement no view 1", "description", "us");
+ private static final MeasureDouble MEASURE_DOUBLE_NO_VIEW_2 =
+ MeasureDouble.create("my measurement no view 2", "description", "us");
+ private static final View.Name VIEW_NAME = View.Name.create("my view");
+ private static final BucketBoundaries BUCKET_BOUNDARIES =
+ BucketBoundaries.create(Arrays.asList(-10.0, 0.0, 10.0));
+ private static final Distribution DISTRIBUTION = Distribution.create(BUCKET_BOUNDARIES);
+ private static final Distribution DISTRIBUTION_NO_HISTOGRAM =
+ Distribution.create(BucketBoundaries.create(Collections.<Double>emptyList()));
+ private static final Timestamp START_TIME = Timestamp.fromMillis(0);
+ private static final Duration ONE_SECOND = Duration.fromMillis(1000);
+
+ private final TestClock testClock = TestClock.create();
+ private final StatsComponent statsComponent =
+ new StatsComponentImplBase(new SimpleEventQueue(), testClock);
+
+ 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 record_UnregisteredMeasure() {
+ View view =
+ View.create(
+ VIEW_NAME,
+ "description",
+ MEASURE_DOUBLE,
+ Sum.create(),
+ Arrays.asList(KEY),
+ Cumulative.create());
+ viewManager.registerView(view);
+ statsRecorder
+ .newMeasureMap()
+ .put(MEASURE_DOUBLE_NO_VIEW_1, 1.0)
+ .put(MEASURE_DOUBLE, 2.0)
+ .put(MEASURE_DOUBLE_NO_VIEW_2, 3.0)
+ .record(new SimpleTagContext(Tag.create(KEY, VALUE)));
+ ViewData viewData = viewManager.getView(VIEW_NAME);
+
+ // There should be one entry.
+ StatsTestUtil.assertAggregationMapEquals(
+ viewData.getAggregationMap(),
+ ImmutableMap.of(
+ Arrays.asList(VALUE),
+ StatsTestUtil.createAggregationData(Sum.create(), MEASURE_DOUBLE, 2.0)),
+ 1e-6);
+ }
+
+ @Test
+ public void record_WithAttachments_Distribution() {
+ testClock.setTime(START_TIME);
+ View view =
+ View.create(VIEW_NAME, "description", MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY));
+ viewManager.registerView(view);
+ recordWithAttachments();
+ ViewData viewData = viewManager.getView(VIEW_NAME);
+ assertThat(viewData).isNotNull();
+ DistributionData distributionData =
+ (DistributionData) viewData.getAggregationMap().get(Collections.singletonList(VALUE));
+ List<Exemplar> expected =
+ Arrays.asList(
+ Exemplar.create(-20.0, Timestamp.create(4, 0), Collections.singletonMap("k3", "v1")),
+ Exemplar.create(-5.0, Timestamp.create(5, 0), Collections.singletonMap("k3", "v3")),
+ Exemplar.create(1.0, Timestamp.create(2, 0), Collections.singletonMap("k2", "v2")),
+ Exemplar.create(12.0, Timestamp.create(3, 0), Collections.singletonMap("k1", "v3")));
+ assertThat(distributionData.getExemplars()).containsExactlyElementsIn(expected).inOrder();
+ }
+
+ @Test
+ public void record_WithAttachments_DistributionNoHistogram() {
+ testClock.setTime(START_TIME);
+ View view =
+ View.create(
+ VIEW_NAME,
+ "description",
+ MEASURE_DOUBLE,
+ DISTRIBUTION_NO_HISTOGRAM,
+ Arrays.asList(KEY));
+ viewManager.registerView(view);
+ recordWithAttachments();
+ ViewData viewData = viewManager.getView(VIEW_NAME);
+ assertThat(viewData).isNotNull();
+ DistributionData distributionData =
+ (DistributionData) viewData.getAggregationMap().get(Collections.singletonList(VALUE));
+ // Recording exemplar has no effect if there's no histogram.
+ assertThat(distributionData.getExemplars()).isEmpty();
+ }
+
+ @Test
+ public void record_WithAttachments_Count() {
+ testClock.setTime(START_TIME);
+ View view =
+ View.create(VIEW_NAME, "description", MEASURE_DOUBLE, Count.create(), Arrays.asList(KEY));
+ viewManager.registerView(view);
+ recordWithAttachments();
+ ViewData viewData = viewManager.getView(VIEW_NAME);
+ assertThat(viewData).isNotNull();
+ CountData countData =
+ (CountData) viewData.getAggregationMap().get(Collections.singletonList(VALUE));
+ // Recording exemplar does not affect views with an aggregation other than distribution.
+ assertThat(countData.getCount()).isEqualTo(6L);
+ }
+
+ private void recordWithAttachments() {
+ TagContext context = new SimpleTagContext(Tag.create(KEY, VALUE));
+
+ // The test Distribution has bucket boundaries [-10.0, 0.0, 10.0].
+
+ testClock.advanceTime(ONE_SECOND); // 1st second.
+ // -1.0 is in the 2nd bucket [-10.0, 0.0).
+ statsRecorder
+ .newMeasureMap()
+ .put(MEASURE_DOUBLE, -1.0)
+ .putAttachment("k1", "v1")
+ .record(context);
+
+ testClock.advanceTime(ONE_SECOND); // 2nd second.
+ // 1.0 is in the 3rd bucket [0.0, 10.0).
+ statsRecorder
+ .newMeasureMap()
+ .put(MEASURE_DOUBLE, 1.0)
+ .putAttachment("k2", "v2")
+ .record(context);
+
+ testClock.advanceTime(ONE_SECOND); // 3rd second.
+ // 12.0 is in the 4th bucket [10.0, +Inf).
+ statsRecorder
+ .newMeasureMap()
+ .put(MEASURE_DOUBLE, 12.0)
+ .putAttachment("k1", "v3")
+ .record(context);
+
+ testClock.advanceTime(ONE_SECOND); // 4th second.
+ // -20.0 is in the 1st bucket [-Inf, -10.0).
+ statsRecorder
+ .newMeasureMap()
+ .put(MEASURE_DOUBLE, -20.0)
+ .putAttachment("k3", "v1")
+ .record(context);
+
+ testClock.advanceTime(ONE_SECOND); // 5th second.
+ // -5.0 is in the 2nd bucket [-10.0, 0), should overwrite the previous exemplar -1.0.
+ statsRecorder
+ .newMeasureMap()
+ .put(MEASURE_DOUBLE, -5.0)
+ .putAttachment("k3", "v3")
+ .record(context);
+
+ testClock.advanceTime(ONE_SECOND); // 6th second.
+ // -3.0 is in the 2nd bucket [-10.0, 0), but this value doesn't come with attachments, so it
+ // shouldn't overwrite the previous exemplar (-5.0).
+ statsRecorder.newMeasureMap().put(MEASURE_DOUBLE, -3.0).record(context);
+ }
+
+ @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
+ @SuppressWarnings("deprecation")
+ 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
+ @SuppressWarnings("deprecation")
+ 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);
+ }
+}
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..ea1bf346
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/StatsTestUtil.java
@@ -0,0 +1,232 @@
+/*
+ * 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 com.google.common.collect.Lists;
+import io.opencensus.common.Function;
+import io.opencensus.common.Functions;
+import io.opencensus.common.Timestamp;
+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.LastValueDataDouble;
+import io.opencensus.stats.AggregationData.LastValueDataLong;
+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.Tag;
+import io.opencensus.tags.TagContext;
+import io.opencensus.tags.TagValue;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import javax.annotation.Nullable;
+
+/** Stats test utilities. */
+final class StatsTestUtil {
+
+ private static final Timestamp EMPTY = Timestamp.create(0, 0);
+
+ 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 =
+ RecordUtils.createMutableAggregation(aggregation, measure);
+ for (double value : values) {
+ mutableAggregation.add(value, Collections.<String, String>emptyMap(), EMPTY);
+ }
+ return mutableAggregation.toAggregationData();
+ }
+
+ /**
+ * 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 (Entry<? extends List<? extends TagValue>, ? extends AggregationData> entry :
+ actual.entrySet()) {
+ assertAggregationDataEquals(expected.get(entry.getKey()), entry.getValue(), 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<DistributionData, Void>() {
+ @Override
+ public Void apply(DistributionData arg) {
+ assertThat(actual).isInstanceOf(DistributionData.class);
+ assertDistributionDataEquals(arg, (DistributionData) actual, tolerance);
+ return null;
+ }
+ },
+ new Function<LastValueDataDouble, Void>() {
+ @Override
+ public Void apply(LastValueDataDouble arg) {
+ assertThat(actual).isInstanceOf(LastValueDataDouble.class);
+ assertThat(((LastValueDataDouble) actual).getLastValue())
+ .isWithin(tolerance)
+ .of(arg.getLastValue());
+ return null;
+ }
+ },
+ new Function<LastValueDataLong, Void>() {
+ @Override
+ public Void apply(LastValueDataLong arg) {
+ assertThat(actual).isInstanceOf(LastValueDataLong.class);
+ assertThat(((LastValueDataLong) actual).getLastValue()).isEqualTo(arg.getLastValue());
+ return null;
+ }
+ },
+ new Function<AggregationData, Void>() {
+ @Override
+ public Void apply(AggregationData arg) {
+ if (arg instanceof MeanData) {
+ assertThat(actual).isInstanceOf(MeanData.class);
+ assertThat(((MeanData) actual).getMean())
+ .isWithin(tolerance)
+ .of(((MeanData) arg).getMean());
+ return null;
+ }
+ throw new IllegalArgumentException("Unknown Aggregation.");
+ }
+ });
+ }
+
+ // 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()));
+ }
+
+ @Nullable
+ 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;
+ }
+
+ 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/ViewManagerImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java
new file mode 100644
index 00000000..a4018b79
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/stats/ViewManagerImplTest.java
@@ -0,0 +1,1021 @@
+/*
+ * 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.LastValue;
+import io.opencensus.stats.Aggregation.Mean;
+import io.opencensus.stats.Aggregation.Sum;
+import io.opencensus.stats.AggregationData;
+import io.opencensus.stats.AggregationData.LastValueDataDouble;
+import io.opencensus.stats.AggregationData.LastValueDataLong;
+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 java.util.Set;
+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 long MILLIS_PER_SECOND = 1000;
+ private static final Duration TEN_SECONDS = Duration.create(10, 0);
+ private static final Interval INTERVAL = Interval.create(TEN_SECONDS);
+
+ 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 static final LastValue LAST_VALUE = LastValue.create();
+
+ 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 testGetAllExportedViews() {
+ assertThat(viewManager.getAllExportedViews()).isEmpty();
+ View cumulativeView1 =
+ createCumulativeView(
+ View.Name.create("View 1"), MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY));
+ View cumulativeView2 =
+ createCumulativeView(
+ View.Name.create("View 2"), MEASURE_DOUBLE, DISTRIBUTION, Arrays.asList(KEY));
+ View intervalView =
+ View.create(
+ View.Name.create("View 3"),
+ VIEW_DESCRIPTION,
+ MEASURE_DOUBLE,
+ DISTRIBUTION,
+ Arrays.asList(KEY),
+ INTERVAL);
+ viewManager.registerView(cumulativeView1);
+ viewManager.registerView(cumulativeView2);
+ viewManager.registerView(intervalView);
+
+ // Only cumulative views should be exported.
+ assertThat(viewManager.getAllExportedViews()).containsExactly(cumulativeView1, cumulativeView2);
+ }
+
+ @Test
+ public void getAllExportedViewsResultIsUnmodifiable() {
+ View view1 =
+ View.create(
+ View.Name.create("View 1"),
+ VIEW_DESCRIPTION,
+ MEASURE_DOUBLE,
+ DISTRIBUTION,
+ Arrays.asList(KEY),
+ CUMULATIVE);
+ viewManager.registerView(view1);
+ Set<View> exported = viewManager.getAllExportedViews();
+
+ View view2 =
+ View.create(
+ View.Name.create("View 2"),
+ VIEW_DESCRIPTION,
+ MEASURE_DOUBLE,
+ DISTRIBUTION,
+ Arrays.asList(KEY),
+ CUMULATIVE);
+ thrown.expect(UnsupportedOperationException.class);
+ exported.add(view2);
+ }
+
+ @Test
+ public void testRegisterAndGetIntervalView() {
+ View intervalView =
+ View.create(
+ VIEW_NAME,
+ VIEW_DESCRIPTION,
+ MEASURE_DOUBLE,
+ DISTRIBUTION,
+ Arrays.asList(KEY),
+ INTERVAL);
+ 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);
+ }
+
+ @Test
+ public void testRecordDouble_lastvalue_cumulative() {
+ testRecordCumulative(MEASURE_DOUBLE, LAST_VALUE, 11.1, 22.2, 33.3, 44.4);
+ }
+
+ @Test
+ public void testRecordLong_lastvalue_cumulative() {
+ testRecordCumulative(MEASURE_LONG, LAST_VALUE, 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[] {10, 24, 30, 40, 50},
+ -50,
+ 30,
+ SumDataLong.create(Math.round(34 * 0.6 + 120)),
+ SumDataLong.create(-40),
+ SumDataLong.create(30));
+ }
+
+ @Test
+ public void testRecordDouble_lastvalue_interval() {
+ testRecordInterval(
+ MEASURE_DOUBLE,
+ LAST_VALUE,
+ new double[] {20.0, -1.0, 1.0, -5.0, 5.0},
+ 9.0,
+ 30.0,
+ LastValueDataDouble.create(5.0),
+ LastValueDataDouble.create(9.0),
+ LastValueDataDouble.create(30.0));
+ }
+
+ @Test
+ public void testRecordLong_lastvalue_interval() {
+ testRecordInterval(
+ MEASURE_LONG,
+ LAST_VALUE,
+ new double[] {1000, 2000, 3000, 4000, 5000},
+ -5000,
+ 30,
+ LastValueDataLong.create(5000),
+ LastValueDataLong.create(-5000),
+ LastValueDataLong.create(30));
+ }
+
+ private final 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(RecordUtils.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(RecordUtils.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
+ @SuppressWarnings("deprecation")
+ 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
+ @SuppressWarnings("deprecation")
+ 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
+ @SuppressWarnings("deprecation")
+ 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
+ @SuppressWarnings("deprecation")
+ 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);
+ }
+
+ @SuppressWarnings("deprecation")
+ 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/ScopedTagContextsTest.java b/impl_core/src/test/java/io/opencensus/implcore/tags/ScopedTagContextsTest.java
new file mode 100644
index 00000000..6a8fe4c7
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/ScopedTagContextsTest.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 io.opencensus.common.Scope;
+import io.opencensus.implcore.internal.CurrentState;
+import io.opencensus.implcore.internal.CurrentState.State;
+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 CurrentState(State.ENABLED));
+
+ @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..1859e081
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TagContextImplTest.java
@@ -0,0 +1,167 @@
+/*
+ * 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 org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.common.testing.EqualsTester;
+import io.opencensus.implcore.internal.CurrentState;
+import io.opencensus.implcore.internal.CurrentState.State;
+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 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}.
+ *
+ * <p>Tests for {@link TagContextBuilderImpl#buildScoped()} are in {@link ScopedTagContextsTest}.
+ */
+@RunWith(JUnit4.class)
+public class TagContextImplTest {
+ private final Tagger tagger = new TaggerImpl(new CurrentState(State.ENABLED));
+
+ private static final TagKey K1 = TagKey.create("k1");
+ private static final TagKey K2 = 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 getTags_empty() {
+ TagContextImpl tags = new TagContextImpl(ImmutableMap.<TagKey, TagValue>of());
+ assertThat(tags.getTags()).isEmpty();
+ }
+
+ @Test
+ public void getTags_nonEmpty() {
+ TagContextImpl tags = new TagContextImpl(ImmutableMap.of(K1, V1, K2, V2));
+ assertThat(tags.getTags()).containsExactly(K1, V1, K2, V2);
+ }
+
+ @Test
+ public void put_newKey() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1));
+ assertThat(((TagContextImpl) tagger.toBuilder(tags).put(K2, V2).build()).getTags())
+ .containsExactly(K1, V1, K2, V2);
+ }
+
+ @Test
+ public void put_existingKey() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1));
+ assertThat(((TagContextImpl) tagger.toBuilder(tags).put(K1, V2).build()).getTags())
+ .containsExactly(K1, V2);
+ }
+
+ @Test
+ public void put_nullKey() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1));
+ TagContextBuilder builder = tagger.toBuilder(tags);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("key");
+ builder.put(null, V2);
+ }
+
+ @Test
+ public void put_nullValue() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1));
+ TagContextBuilder builder = tagger.toBuilder(tags);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("value");
+ builder.put(K2, null);
+ }
+
+ @Test
+ public void remove_existingKey() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1, K2, V2));
+ assertThat(((TagContextImpl) tagger.toBuilder(tags).remove(K1).build()).getTags())
+ .containsExactly(K2, V2);
+ }
+
+ @Test
+ public void remove_differentKey() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1));
+ assertThat(((TagContextImpl) tagger.toBuilder(tags).remove(K2).build()).getTags())
+ .containsExactly(K1, V1);
+ }
+
+ @Test
+ public void remove_nullKey() {
+ TagContext tags = new TagContextImpl(ImmutableMap.of(K1, V1));
+ TagContextBuilder builder = tagger.toBuilder(tags);
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("key");
+ builder.remove(null);
+ }
+
+ @Test
+ public void testIterator() {
+ TagContextImpl tags = new TagContextImpl(ImmutableMap.of(K1, V1, K2, V2));
+ Iterator<Tag> i = tags.getIterator();
+ 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(K1, V1), Tag.create(K2, V2));
+ thrown.expect(NoSuchElementException.class);
+ i.next();
+ }
+
+ @Test
+ public void disallowCallingRemoveOnIterator() {
+ TagContextImpl tags = new TagContextImpl(ImmutableMap.of(K1, V1, K2, V2));
+ Iterator<Tag> i = tags.getIterator();
+ i.next();
+ thrown.expect(UnsupportedOperationException.class);
+ i.remove();
+ }
+
+ @Test
+ public void testEquals() {
+ new EqualsTester()
+ .addEqualityGroup(
+ tagger.emptyBuilder().put(K1, V1).put(K2, V2).build(),
+ tagger.emptyBuilder().put(K1, V1).put(K2, V2).build(),
+ tagger.emptyBuilder().put(K2, V2).put(K1, V1).build(),
+ new TagContext() {
+ @Override
+ protected Iterator<Tag> getIterator() {
+ return Lists.<Tag>newArrayList(Tag.create(K1, V1), Tag.create(K2, V2)).iterator();
+ }
+ })
+ .addEqualityGroup(tagger.emptyBuilder().put(K1, V1).put(K2, V1).build())
+ .addEqualityGroup(tagger.emptyBuilder().put(K1, V2).put(K2, 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..1bc13c59
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TagsComponentImplBaseTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link TagsComponentImplBase}. */
+@RunWith(JUnit4.class)
+public class TagsComponentImplBaseTest {
+
+ @Rule public final ExpectedException thrown = ExpectedException.none();
+
+ private final TagsComponent tagsComponent = new TagsComponentImplBase();
+
+ @Test
+ public void defaultState() {
+ assertThat(tagsComponent.getState()).isEqualTo(TaggingState.ENABLED);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void setState_Disabled() {
+ tagsComponent.setState(TaggingState.DISABLED);
+ assertThat(tagsComponent.getState()).isEqualTo(TaggingState.DISABLED);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void setState_Enabled() {
+ tagsComponent.setState(TaggingState.DISABLED);
+ tagsComponent.setState(TaggingState.ENABLED);
+ assertThat(tagsComponent.getState()).isEqualTo(TaggingState.ENABLED);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void setState_DisallowsNull() {
+ thrown.expect(NullPointerException.class);
+ thrown.expectMessage("newState");
+ tagsComponent.setState(null);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void preventSettingStateAfterGettingState_DifferentState() {
+ tagsComponent.setState(TaggingState.DISABLED);
+ tagsComponent.getState();
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("State was already read, cannot set state.");
+ tagsComponent.setState(TaggingState.ENABLED);
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ public void preventSettingStateAfterGettingState_SameState() {
+ tagsComponent.setState(TaggingState.DISABLED);
+ tagsComponent.getState();
+ thrown.expect(IllegalStateException.class);
+ thrown.expectMessage("State was already read, cannot set state.");
+ tagsComponent.setState(TaggingState.DISABLED);
+ }
+}
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..dcfba508
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/TagsTestUtil.java
@@ -0,0 +1,33 @@
+/*
+ * 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 {
+ private 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..26a072f6
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextBinarySerializerImplTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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
+ @SuppressWarnings("deprecation")
+ public void toByteArray_TaggingDisabled() throws TagContextSerializationException {
+ tagsComponent.setState(TaggingState.DISABLED);
+ assertThat(serializer.toByteArray(tagContext)).isEmpty();
+ }
+
+ @Test
+ @SuppressWarnings("deprecation")
+ 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
+ @SuppressWarnings("deprecation")
+ 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..8db0e389
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextDeserializationTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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 testConstants() {
+ // 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);
+ assertThat(SerializationUtils.TAGCONTEXT_SERIALIZED_SIZE_LIMIT).isEqualTo(8192);
+ }
+
+ @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 testDeserializeTooLargeByteArrayThrowException()
+ throws TagContextDeserializationException {
+ ByteArrayDataOutput output = ByteStreams.newDataOutput();
+ output.write(SerializationUtils.VERSION_ID);
+ for (int i = 0; i < SerializationUtils.TAGCONTEXT_SERIALIZED_SIZE_LIMIT / 8 - 1; i++) {
+ // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8.
+ String str;
+ if (i < 10) {
+ str = "000" + i;
+ } else if (i < 100) {
+ str = "00" + i;
+ } else if (i < 1000) {
+ str = "0" + i;
+ } else {
+ str = String.valueOf(i);
+ }
+ encodeTagToOutput(str, str, output);
+ }
+ // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte
+ // more than limit.
+ encodeTagToOutput("last", "last1", output);
+
+ byte[] bytes = output.toByteArray();
+ thrown.expect(TagContextDeserializationException.class);
+ thrown.expectMessage("Size of TagContext exceeds the maximum serialized size ");
+ serializer.fromByteArray(bytes);
+ }
+
+ // Deserializing this input should cause an error, even though it represents a relatively small
+ // TagContext.
+ @Test
+ public void testDeserializeTooLargeByteArrayThrowException_WithDuplicateTagKeys()
+ throws TagContextDeserializationException {
+ ByteArrayDataOutput output = ByteStreams.newDataOutput();
+ output.write(SerializationUtils.VERSION_ID);
+ for (int i = 0; i < SerializationUtils.TAGCONTEXT_SERIALIZED_SIZE_LIMIT / 8 - 1; i++) {
+ // Each tag will be with format {key : "key_", value : "0123"}, so the length of it is 8.
+ String str;
+ if (i < 10) {
+ str = "000" + i;
+ } else if (i < 100) {
+ str = "00" + i;
+ } else if (i < 1000) {
+ str = "0" + i;
+ } else {
+ str = String.valueOf(i);
+ }
+ encodeTagToOutput("key_", str, output);
+ }
+ // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte
+ // more than limit.
+ encodeTagToOutput("key_", "last1", output);
+
+ byte[] bytes = output.toByteArray();
+ thrown.expect(TagContextDeserializationException.class);
+ thrown.expectMessage("Size of TagContext exceeds the maximum serialized size ");
+ serializer.fromByteArray(bytes);
+ }
+
+ @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 testDeserializeDuplicateKeys() throws TagContextDeserializationException {
+ ByteArrayDataOutput output = ByteStreams.newDataOutput();
+ output.write(SerializationUtils.VERSION_ID);
+ encodeTagToOutput("Key1", "Value1", output);
+ encodeTagToOutput("Key1", "Value2", output);
+ TagContext expected =
+ tagger.emptyBuilder().put(TagKey.create("Key1"), TagValue.create("Value2")).build();
+ assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(expected);
+ }
+
+ @Test
+ public void testDeserializeNonConsecutiveDuplicateKeys()
+ throws TagContextDeserializationException {
+ ByteArrayDataOutput output = ByteStreams.newDataOutput();
+ output.write(SerializationUtils.VERSION_ID);
+ encodeTagToOutput("Key1", "Value1", output);
+ encodeTagToOutput("Key2", "Value2", output);
+ encodeTagToOutput("Key3", "Value3", output);
+ encodeTagToOutput("Key1", "Value4", output);
+ encodeTagToOutput("Key2", "Value5", output);
+ TagContext expected =
+ tagger
+ .emptyBuilder()
+ .put(TagKey.create("Key1"), TagValue.create("Value4"))
+ .put(TagKey.create("Key2"), TagValue.create("Value5"))
+ .put(TagKey.create("Key3"), TagValue.create("Value3"))
+ .build();
+ assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(expected);
+ }
+
+ @Test
+ public void testDeserializeDuplicateTags() throws TagContextDeserializationException {
+ ByteArrayDataOutput output = ByteStreams.newDataOutput();
+ output.write(SerializationUtils.VERSION_ID);
+ encodeTagToOutput("Key1", "Value1", output);
+ encodeTagToOutput("Key1", "Value1", output);
+ TagContext expected =
+ tagger.emptyBuilder().put(TagKey.create("Key1"), TagValue.create("Value1")).build();
+ assertThat(serializer.fromByteArray(output.toByteArray())).isEqualTo(expected);
+ }
+
+ @Test
+ public void testDeserializeNonConsecutiveDuplicateTags()
+ throws TagContextDeserializationException {
+ ByteArrayDataOutput output = ByteStreams.newDataOutput();
+ output.write(SerializationUtils.VERSION_ID);
+ encodeTagToOutput("Key1", "Value1", output);
+ encodeTagToOutput("Key2", "Value2", output);
+ encodeTagToOutput("Key3", "Value3", output);
+ 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"))
+ .put(TagKey.create("Key3"), TagValue.create("Value3"))
+ .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 supports version up to: 0");
+ serializer.fromByteArray(new byte[] {(byte) (SerializationUtils.VERSION_ID + 1)});
+ }
+
+ @Test
+ public void testDeserializeNegativeVersionId() throws TagContextDeserializationException {
+ thrown.expect(TagContextDeserializationException.class);
+ thrown.expectMessage("Wrong Version ID: -1. Currently supports version up to: 0");
+ serializer.fromByteArray(new byte[] {(byte) -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..1b1aa042
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextRoundtripTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.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 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_NormalTagContext() 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());
+ }
+
+ @Test
+ public void testRoundtrip_TagContextWithMaximumSize() throws Exception {
+ TagContextBuilder builder = tagger.emptyBuilder();
+ for (int i = 0; i < SerializationUtils.TAGCONTEXT_SERIALIZED_SIZE_LIMIT / 8; i++) {
+ // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8.
+ // Add 1024 tags, the total size should just be 8192.
+ String str;
+ if (i < 10) {
+ str = "000" + i;
+ } else if (i < 100) {
+ str = "00" + i;
+ } else if (i < 1000) {
+ str = "0" + i;
+ } else {
+ str = "" + i;
+ }
+ builder.put(TagKey.create(str), TagValue.create(str));
+ }
+ testRoundtripSerialization(builder.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..ed68fe3d
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/tags/propagation/TagContextSerializationTest.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.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.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.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.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+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 {
+
+ @Rule public final ExpectedException thrown = ExpectedException.none();
+
+ 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);
+ }
+
+ @Test
+ public void testSerializeTooLargeTagContext() throws TagContextSerializationException {
+ TagContextBuilder builder = tagger.emptyBuilder();
+ for (int i = 0; i < SerializationUtils.TAGCONTEXT_SERIALIZED_SIZE_LIMIT / 8 - 1; i++) {
+ // Each tag will be with format {key : "0123", value : "0123"}, so the length of it is 8.
+ String str;
+ if (i < 10) {
+ str = "000" + i;
+ } else if (i < 100) {
+ str = "00" + i;
+ } else if (i < 1000) {
+ str = "0" + i;
+ } else {
+ str = String.valueOf(i);
+ }
+ builder.put(TagKey.create(str), TagValue.create(str));
+ }
+ // The last tag will be of size 9, so the total size of the TagContext (8193) will be one byte
+ // more than limit.
+ builder.put(TagKey.create("last"), TagValue.create("last1"));
+
+ TagContext tagContext = builder.build();
+ thrown.expect(TagContextSerializationException.class);
+ thrown.expectMessage("Size of TagContext exceeds the maximum serialized size ");
+ serializer.toByteArray(tagContext);
+ }
+
+ 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(SerializationUtils.VERSION_ID);
+ for (Tag tag : list) {
+ expected.write(SerializationUtils.TAG_FIELD_ID);
+ encodeString(tag.getKey().getName(), expected);
+ encodeString(tag.getValue().asString(), expected);
+ }
+ possibleOutputs.add(new String(expected.toByteArray(), Charsets.UTF_8));
+ }
+
+ 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));
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/NoRecordEventsSpanImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/NoRecordEventsSpanImplTest.java
new file mode 100644
index 00000000..c576860d
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/NoRecordEventsSpanImplTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2018, 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.trace;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.trace.Annotation;
+import io.opencensus.trace.AttributeValue;
+import io.opencensus.trace.EndSpanOptions;
+import io.opencensus.trace.Link;
+import io.opencensus.trace.MessageEvent;
+import io.opencensus.trace.NetworkEvent;
+import io.opencensus.trace.Span.Options;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.Status;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.Tracestate;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link NoRecordEventsSpanImpl}. */
+@RunWith(JUnit4.class)
+public class NoRecordEventsSpanImplTest {
+ private final Random random = new Random(1234);
+ private final SpanContext spanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random),
+ SpanId.generateRandomId(random),
+ TraceOptions.DEFAULT,
+ Tracestate.builder().build());
+ private final NoRecordEventsSpanImpl noRecordEventsSpan =
+ NoRecordEventsSpanImpl.create(spanContext);
+
+ @Test
+ public void propagatesSpanContext() {
+ assertThat(noRecordEventsSpan.getContext()).isEqualTo(spanContext);
+ }
+
+ @Test
+ public void hasNoRecordEventsOption() {
+ assertThat(noRecordEventsSpan.getOptions()).doesNotContain(Options.RECORD_EVENTS);
+ }
+
+ @Test
+ public void doNotCrash() {
+ Map<String, AttributeValue> attributes = new HashMap<String, AttributeValue>();
+ attributes.put(
+ "MyStringAttributeKey", AttributeValue.stringAttributeValue("MyStringAttributeValue"));
+ Map<String, AttributeValue> multipleAttributes = new HashMap<String, AttributeValue>();
+ multipleAttributes.put(
+ "MyStringAttributeKey", AttributeValue.stringAttributeValue("MyStringAttributeValue"));
+ multipleAttributes.put("MyBooleanAttributeKey", AttributeValue.booleanAttributeValue(true));
+ multipleAttributes.put("MyLongAttributeKey", AttributeValue.longAttributeValue(123));
+ // Tests only that all the methods are not crashing/throwing errors.
+ noRecordEventsSpan.putAttribute(
+ "MyStringAttributeKey2", AttributeValue.stringAttributeValue("MyStringAttributeValue2"));
+ noRecordEventsSpan.addAttributes(attributes);
+ noRecordEventsSpan.addAttributes(multipleAttributes);
+ noRecordEventsSpan.addAnnotation("MyAnnotation");
+ noRecordEventsSpan.addAnnotation("MyAnnotation", attributes);
+ noRecordEventsSpan.addAnnotation("MyAnnotation", multipleAttributes);
+ noRecordEventsSpan.addAnnotation(Annotation.fromDescription("MyAnnotation"));
+ noRecordEventsSpan.addNetworkEvent(NetworkEvent.builder(NetworkEvent.Type.SENT, 1L).build());
+ noRecordEventsSpan.addMessageEvent(MessageEvent.builder(MessageEvent.Type.SENT, 1L).build());
+ noRecordEventsSpan.addLink(
+ Link.fromSpanContext(SpanContext.INVALID, Link.Type.CHILD_LINKED_SPAN));
+ noRecordEventsSpan.setStatus(Status.OK);
+ noRecordEventsSpan.end(EndSpanOptions.DEFAULT);
+ noRecordEventsSpan.end();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/RecordEventsSpanImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/RecordEventsSpanImplTest.java
new file mode 100644
index 00000000..b293a225
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/RecordEventsSpanImplTest.java
@@ -0,0 +1,594 @@
+/*
+ * 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.trace;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Duration;
+import io.opencensus.common.Timestamp;
+import io.opencensus.implcore.internal.TimestampConverter;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.testing.common.TestClock;
+import io.opencensus.trace.Annotation;
+import io.opencensus.trace.AttributeValue;
+import io.opencensus.trace.EndSpanOptions;
+import io.opencensus.trace.Link;
+import io.opencensus.trace.NetworkEvent;
+import io.opencensus.trace.Span.Kind;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.Status;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SpanData;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link RecordEventsSpanImpl}. */
+@RunWith(JUnit4.class)
+public class RecordEventsSpanImplTest {
+ private static final String SPAN_NAME = "MySpanName";
+ private static final String ANNOTATION_DESCRIPTION = "MyAnnotation";
+ private final Random random = new Random(1234);
+ private final SpanContext spanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random), SpanId.generateRandomId(random), TraceOptions.DEFAULT);
+ private final SpanId parentSpanId = SpanId.generateRandomId(random);
+ private final Timestamp timestamp = Timestamp.create(1234, 5678);
+ private final TestClock testClock = TestClock.create(timestamp);
+ private final TimestampConverter timestampConverter = TimestampConverter.now(testClock);
+ private final Map<String, AttributeValue> attributes = new HashMap<String, AttributeValue>();
+ private final Map<String, AttributeValue> expectedAttributes =
+ new HashMap<String, AttributeValue>();
+ @Mock private StartEndHandler startEndHandler;
+ @Rule public final ExpectedException exception = ExpectedException.none();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ attributes.put(
+ "MyStringAttributeKey", AttributeValue.stringAttributeValue("MyStringAttributeValue"));
+ attributes.put("MyLongAttributeKey", AttributeValue.longAttributeValue(123L));
+ attributes.put("MyBooleanAttributeKey", AttributeValue.booleanAttributeValue(false));
+ expectedAttributes.putAll(attributes);
+ expectedAttributes.put(
+ "MySingleStringAttributeKey",
+ AttributeValue.stringAttributeValue("MySingleStringAttributeValue"));
+ }
+
+ @Test
+ public void noEventsRecordedAfterEnd() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ span.end();
+ // Check that adding trace events after Span#end() does not throw any exception and are not
+ // recorded.
+ span.putAttributes(attributes);
+ span.putAttribute(
+ "MySingleStringAttributeKey",
+ AttributeValue.stringAttributeValue("MySingleStringAttributeValue"));
+ span.addAnnotation(Annotation.fromDescription(ANNOTATION_DESCRIPTION));
+ span.addAnnotation(ANNOTATION_DESCRIPTION, attributes);
+ span.addNetworkEvent(
+ NetworkEvent.builder(NetworkEvent.Type.RECV, 1).setUncompressedMessageSize(3).build());
+ span.addLink(Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN));
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getStartTimestamp()).isEqualTo(timestamp);
+ assertThat(spanData.getAttributes().getAttributeMap()).isEmpty();
+ assertThat(spanData.getAnnotations().getEvents()).isEmpty();
+ assertThat(spanData.getNetworkEvents().getEvents()).isEmpty();
+ assertThat(spanData.getLinks().getLinks()).isEmpty();
+ assertThat(spanData.getStatus()).isEqualTo(Status.OK);
+ assertThat(spanData.getEndTimestamp()).isEqualTo(timestamp);
+ }
+
+ @Test
+ public void deprecatedAddAttributesStillWorks() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ span.addAttributes(attributes);
+ span.end();
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getAttributes().getAttributeMap()).isEqualTo(attributes);
+ }
+
+ @Test
+ public void toSpanData_ActiveSpan() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ true,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ Mockito.verify(startEndHandler, Mockito.times(1)).onStart(span);
+ span.putAttribute(
+ "MySingleStringAttributeKey",
+ AttributeValue.stringAttributeValue("MySingleStringAttributeValue"));
+ span.putAttributes(attributes);
+ testClock.advanceTime(Duration.create(0, 100));
+ span.addAnnotation(Annotation.fromDescription(ANNOTATION_DESCRIPTION));
+ testClock.advanceTime(Duration.create(0, 100));
+ span.addAnnotation(ANNOTATION_DESCRIPTION, attributes);
+ testClock.advanceTime(Duration.create(0, 100));
+ NetworkEvent networkEvent =
+ NetworkEvent.builder(NetworkEvent.Type.RECV, 1).setUncompressedMessageSize(3).build();
+ span.addNetworkEvent(networkEvent);
+ testClock.advanceTime(Duration.create(0, 100));
+ Link link = Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN);
+ span.addLink(link);
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getContext()).isEqualTo(spanContext);
+ assertThat(spanData.getName()).isEqualTo(SPAN_NAME);
+ assertThat(spanData.getParentSpanId()).isEqualTo(parentSpanId);
+ assertThat(spanData.getHasRemoteParent()).isTrue();
+ assertThat(spanData.getAttributes().getDroppedAttributesCount()).isEqualTo(0);
+ assertThat(spanData.getAttributes().getAttributeMap()).isEqualTo(expectedAttributes);
+ assertThat(spanData.getAnnotations().getDroppedEventsCount()).isEqualTo(0);
+ assertThat(spanData.getAnnotations().getEvents().size()).isEqualTo(2);
+ assertThat(spanData.getAnnotations().getEvents().get(0).getTimestamp())
+ .isEqualTo(timestamp.addNanos(100));
+ assertThat(spanData.getAnnotations().getEvents().get(0).getEvent())
+ .isEqualTo(Annotation.fromDescription(ANNOTATION_DESCRIPTION));
+ assertThat(spanData.getAnnotations().getEvents().get(1).getTimestamp())
+ .isEqualTo(timestamp.addNanos(200));
+ assertThat(spanData.getAnnotations().getEvents().get(1).getEvent())
+ .isEqualTo(Annotation.fromDescriptionAndAttributes(ANNOTATION_DESCRIPTION, attributes));
+ assertThat(spanData.getNetworkEvents().getDroppedEventsCount()).isEqualTo(0);
+ assertThat(spanData.getNetworkEvents().getEvents().size()).isEqualTo(1);
+ assertThat(spanData.getNetworkEvents().getEvents().get(0).getTimestamp())
+ .isEqualTo(timestamp.addNanos(300));
+ assertThat(spanData.getNetworkEvents().getEvents().get(0).getEvent()).isEqualTo(networkEvent);
+ assertThat(spanData.getLinks().getDroppedLinksCount()).isEqualTo(0);
+ assertThat(spanData.getLinks().getLinks().size()).isEqualTo(1);
+ assertThat(spanData.getLinks().getLinks().get(0)).isEqualTo(link);
+ assertThat(spanData.getStartTimestamp()).isEqualTo(timestamp);
+ assertThat(spanData.getStatus()).isNull();
+ assertThat(spanData.getEndTimestamp()).isNull();
+ }
+
+ @Test
+ public void toSpanData_EndedSpan() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ Mockito.verify(startEndHandler, Mockito.times(1)).onStart(span);
+ span.putAttribute(
+ "MySingleStringAttributeKey",
+ AttributeValue.stringAttributeValue("MySingleStringAttributeValue"));
+ span.putAttributes(attributes);
+ testClock.advanceTime(Duration.create(0, 100));
+ span.addAnnotation(Annotation.fromDescription(ANNOTATION_DESCRIPTION));
+ testClock.advanceTime(Duration.create(0, 100));
+ span.addAnnotation(ANNOTATION_DESCRIPTION, attributes);
+ testClock.advanceTime(Duration.create(0, 100));
+ NetworkEvent networkEvent =
+ NetworkEvent.builder(NetworkEvent.Type.RECV, 1).setUncompressedMessageSize(3).build();
+ span.addNetworkEvent(networkEvent);
+ Link link = Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN);
+ span.addLink(link);
+ testClock.advanceTime(Duration.create(0, 100));
+ span.end(EndSpanOptions.builder().setStatus(Status.CANCELLED).build());
+ Mockito.verify(startEndHandler, Mockito.times(1)).onEnd(span);
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getContext()).isEqualTo(spanContext);
+ assertThat(spanData.getName()).isEqualTo(SPAN_NAME);
+ assertThat(spanData.getParentSpanId()).isEqualTo(parentSpanId);
+ assertThat(spanData.getHasRemoteParent()).isFalse();
+ assertThat(spanData.getAttributes().getDroppedAttributesCount()).isEqualTo(0);
+ assertThat(spanData.getAttributes().getAttributeMap()).isEqualTo(expectedAttributes);
+ assertThat(spanData.getAnnotations().getDroppedEventsCount()).isEqualTo(0);
+ assertThat(spanData.getAnnotations().getEvents().size()).isEqualTo(2);
+ assertThat(spanData.getAnnotations().getEvents().get(0).getTimestamp())
+ .isEqualTo(timestamp.addNanos(100));
+ assertThat(spanData.getAnnotations().getEvents().get(0).getEvent())
+ .isEqualTo(Annotation.fromDescription(ANNOTATION_DESCRIPTION));
+ assertThat(spanData.getAnnotations().getEvents().get(1).getTimestamp())
+ .isEqualTo(timestamp.addNanos(200));
+ assertThat(spanData.getAnnotations().getEvents().get(1).getEvent())
+ .isEqualTo(Annotation.fromDescriptionAndAttributes(ANNOTATION_DESCRIPTION, attributes));
+ assertThat(spanData.getNetworkEvents().getDroppedEventsCount()).isEqualTo(0);
+ assertThat(spanData.getNetworkEvents().getEvents().size()).isEqualTo(1);
+ assertThat(spanData.getNetworkEvents().getEvents().get(0).getTimestamp())
+ .isEqualTo(timestamp.addNanos(300));
+ assertThat(spanData.getNetworkEvents().getEvents().get(0).getEvent()).isEqualTo(networkEvent);
+ assertThat(spanData.getLinks().getDroppedLinksCount()).isEqualTo(0);
+ assertThat(spanData.getLinks().getLinks().size()).isEqualTo(1);
+ assertThat(spanData.getLinks().getLinks().get(0)).isEqualTo(link);
+ assertThat(spanData.getStartTimestamp()).isEqualTo(timestamp);
+ assertThat(spanData.getStatus()).isEqualTo(Status.CANCELLED);
+ assertThat(spanData.getEndTimestamp()).isEqualTo(timestamp.addNanos(400));
+ }
+
+ @Test
+ public void status_ViaSetStatus() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ Mockito.verify(startEndHandler, Mockito.times(1)).onStart(span);
+ testClock.advanceTime(Duration.create(0, 100));
+ assertThat(span.getStatus()).isEqualTo(Status.OK);
+ span.setStatus(Status.CANCELLED);
+ assertThat(span.getStatus()).isEqualTo(Status.CANCELLED);
+ span.end();
+ assertThat(span.getStatus()).isEqualTo(Status.CANCELLED);
+ }
+
+ @Test
+ public void status_ViaEndSpanOptions() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ Mockito.verify(startEndHandler, Mockito.times(1)).onStart(span);
+ testClock.advanceTime(Duration.create(0, 100));
+ assertThat(span.getStatus()).isEqualTo(Status.OK);
+ span.setStatus(Status.CANCELLED);
+ assertThat(span.getStatus()).isEqualTo(Status.CANCELLED);
+ span.end(EndSpanOptions.builder().setStatus(Status.ABORTED).build());
+ assertThat(span.getStatus()).isEqualTo(Status.ABORTED);
+ }
+
+ @Test
+ public void droppingAttributes() {
+ final int maxNumberOfAttributes = 8;
+ TraceParams traceParams =
+ TraceParams.DEFAULT.toBuilder().setMaxNumberOfAttributes(maxNumberOfAttributes).build();
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ traceParams,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ for (int i = 0; i < 2 * maxNumberOfAttributes; i++) {
+ Map<String, AttributeValue> attributes = new HashMap<String, AttributeValue>();
+ attributes.put("MyStringAttributeKey" + i, AttributeValue.longAttributeValue(i));
+ span.putAttributes(attributes);
+ }
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getAttributes().getDroppedAttributesCount())
+ .isEqualTo(maxNumberOfAttributes);
+ assertThat(spanData.getAttributes().getAttributeMap().size()).isEqualTo(maxNumberOfAttributes);
+ for (int i = 0; i < maxNumberOfAttributes; i++) {
+ assertThat(
+ spanData
+ .getAttributes()
+ .getAttributeMap()
+ .get("MyStringAttributeKey" + (i + maxNumberOfAttributes)))
+ .isEqualTo(AttributeValue.longAttributeValue(i + maxNumberOfAttributes));
+ }
+ span.end();
+ spanData = span.toSpanData();
+ assertThat(spanData.getAttributes().getDroppedAttributesCount())
+ .isEqualTo(maxNumberOfAttributes);
+ assertThat(spanData.getAttributes().getAttributeMap().size()).isEqualTo(maxNumberOfAttributes);
+ for (int i = 0; i < maxNumberOfAttributes; i++) {
+ assertThat(
+ spanData
+ .getAttributes()
+ .getAttributeMap()
+ .get("MyStringAttributeKey" + (i + maxNumberOfAttributes)))
+ .isEqualTo(AttributeValue.longAttributeValue(i + maxNumberOfAttributes));
+ }
+ }
+
+ @Test
+ public void droppingAndAddingAttributes() {
+ final int maxNumberOfAttributes = 8;
+ TraceParams traceParams =
+ TraceParams.DEFAULT.toBuilder().setMaxNumberOfAttributes(maxNumberOfAttributes).build();
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ traceParams,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ for (int i = 0; i < 2 * maxNumberOfAttributes; i++) {
+ Map<String, AttributeValue> attributes = new HashMap<String, AttributeValue>();
+ attributes.put("MyStringAttributeKey" + i, AttributeValue.longAttributeValue(i));
+ span.putAttributes(attributes);
+ }
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getAttributes().getDroppedAttributesCount())
+ .isEqualTo(maxNumberOfAttributes);
+ assertThat(spanData.getAttributes().getAttributeMap().size()).isEqualTo(maxNumberOfAttributes);
+ for (int i = 0; i < maxNumberOfAttributes; i++) {
+ assertThat(
+ spanData
+ .getAttributes()
+ .getAttributeMap()
+ .get("MyStringAttributeKey" + (i + maxNumberOfAttributes)))
+ .isEqualTo(AttributeValue.longAttributeValue(i + maxNumberOfAttributes));
+ }
+ for (int i = 0; i < maxNumberOfAttributes / 2; i++) {
+ Map<String, AttributeValue> attributes = new HashMap<String, AttributeValue>();
+ attributes.put("MyStringAttributeKey" + i, AttributeValue.longAttributeValue(i));
+ span.putAttributes(attributes);
+ }
+ spanData = span.toSpanData();
+ assertThat(spanData.getAttributes().getDroppedAttributesCount())
+ .isEqualTo(maxNumberOfAttributes * 3 / 2);
+ assertThat(spanData.getAttributes().getAttributeMap().size()).isEqualTo(maxNumberOfAttributes);
+ // Test that we still have in the attributes map the latest maxNumberOfAttributes / 2 entries.
+ for (int i = 0; i < maxNumberOfAttributes / 2; i++) {
+ assertThat(
+ spanData
+ .getAttributes()
+ .getAttributeMap()
+ .get("MyStringAttributeKey" + (i + maxNumberOfAttributes * 3 / 2)))
+ .isEqualTo(AttributeValue.longAttributeValue(i + maxNumberOfAttributes * 3 / 2));
+ }
+ // Test that we have the newest re-added initial entries.
+ for (int i = 0; i < maxNumberOfAttributes / 2; i++) {
+ assertThat(spanData.getAttributes().getAttributeMap().get("MyStringAttributeKey" + i))
+ .isEqualTo(AttributeValue.longAttributeValue(i));
+ }
+ }
+
+ @Test
+ public void droppingAnnotations() {
+ final int maxNumberOfAnnotations = 8;
+ TraceParams traceParams =
+ TraceParams.DEFAULT.toBuilder().setMaxNumberOfAnnotations(maxNumberOfAnnotations).build();
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ traceParams,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ Annotation annotation = Annotation.fromDescription(ANNOTATION_DESCRIPTION);
+ for (int i = 0; i < 2 * maxNumberOfAnnotations; i++) {
+ span.addAnnotation(annotation);
+ testClock.advanceTime(Duration.create(0, 100));
+ }
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getAnnotations().getDroppedEventsCount()).isEqualTo(maxNumberOfAnnotations);
+ assertThat(spanData.getAnnotations().getEvents().size()).isEqualTo(maxNumberOfAnnotations);
+ for (int i = 0; i < maxNumberOfAnnotations; i++) {
+ assertThat(spanData.getAnnotations().getEvents().get(i).getTimestamp())
+ .isEqualTo(timestamp.addNanos(100L * (maxNumberOfAnnotations + i)));
+ assertThat(spanData.getAnnotations().getEvents().get(i).getEvent()).isEqualTo(annotation);
+ }
+ span.end();
+ spanData = span.toSpanData();
+ assertThat(spanData.getAnnotations().getDroppedEventsCount()).isEqualTo(maxNumberOfAnnotations);
+ assertThat(spanData.getAnnotations().getEvents().size()).isEqualTo(maxNumberOfAnnotations);
+ for (int i = 0; i < maxNumberOfAnnotations; i++) {
+ assertThat(spanData.getAnnotations().getEvents().get(i).getTimestamp())
+ .isEqualTo(timestamp.addNanos(100L * (maxNumberOfAnnotations + i)));
+ assertThat(spanData.getAnnotations().getEvents().get(i).getEvent()).isEqualTo(annotation);
+ }
+ }
+
+ @Test
+ public void droppingNetworkEvents() {
+ final int maxNumberOfNetworkEvents = 8;
+ TraceParams traceParams =
+ TraceParams.DEFAULT
+ .toBuilder()
+ .setMaxNumberOfNetworkEvents(maxNumberOfNetworkEvents)
+ .build();
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ traceParams,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ NetworkEvent networkEvent =
+ NetworkEvent.builder(NetworkEvent.Type.RECV, 1).setUncompressedMessageSize(3).build();
+ for (int i = 0; i < 2 * maxNumberOfNetworkEvents; i++) {
+ span.addNetworkEvent(networkEvent);
+ testClock.advanceTime(Duration.create(0, 100));
+ }
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getNetworkEvents().getDroppedEventsCount())
+ .isEqualTo(maxNumberOfNetworkEvents);
+ assertThat(spanData.getNetworkEvents().getEvents().size()).isEqualTo(maxNumberOfNetworkEvents);
+ for (int i = 0; i < maxNumberOfNetworkEvents; i++) {
+ assertThat(spanData.getNetworkEvents().getEvents().get(i).getTimestamp())
+ .isEqualTo(timestamp.addNanos(100L * (maxNumberOfNetworkEvents + i)));
+ assertThat(spanData.getNetworkEvents().getEvents().get(i).getEvent()).isEqualTo(networkEvent);
+ }
+ span.end();
+ spanData = span.toSpanData();
+ assertThat(spanData.getNetworkEvents().getDroppedEventsCount())
+ .isEqualTo(maxNumberOfNetworkEvents);
+ assertThat(spanData.getNetworkEvents().getEvents().size()).isEqualTo(maxNumberOfNetworkEvents);
+ for (int i = 0; i < maxNumberOfNetworkEvents; i++) {
+ assertThat(spanData.getNetworkEvents().getEvents().get(i).getTimestamp())
+ .isEqualTo(timestamp.addNanos(100L * (maxNumberOfNetworkEvents + i)));
+ assertThat(spanData.getNetworkEvents().getEvents().get(i).getEvent()).isEqualTo(networkEvent);
+ }
+ }
+
+ @Test
+ public void droppingLinks() {
+ final int maxNumberOfLinks = 8;
+ TraceParams traceParams =
+ TraceParams.DEFAULT.toBuilder().setMaxNumberOfLinks(maxNumberOfLinks).build();
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ traceParams,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ Link link = Link.fromSpanContext(spanContext, Link.Type.CHILD_LINKED_SPAN);
+ for (int i = 0; i < 2 * maxNumberOfLinks; i++) {
+ span.addLink(link);
+ }
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getLinks().getDroppedLinksCount()).isEqualTo(maxNumberOfLinks);
+ assertThat(spanData.getLinks().getLinks().size()).isEqualTo(maxNumberOfLinks);
+ for (int i = 0; i < maxNumberOfLinks; i++) {
+ assertThat(spanData.getLinks().getLinks().get(i)).isEqualTo(link);
+ }
+ span.end();
+ spanData = span.toSpanData();
+ assertThat(spanData.getLinks().getDroppedLinksCount()).isEqualTo(maxNumberOfLinks);
+ assertThat(spanData.getLinks().getLinks().size()).isEqualTo(maxNumberOfLinks);
+ for (int i = 0; i < maxNumberOfLinks; i++) {
+ assertThat(spanData.getLinks().getLinks().get(i)).isEqualTo(link);
+ }
+ }
+
+ @Test
+ public void sampleToLocalSpanStore() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ span.end(EndSpanOptions.builder().setSampleToLocalSpanStore(true).build());
+ Mockito.verify(startEndHandler, Mockito.times(1)).onEnd(span);
+ assertThat(span.getSampleToLocalSpanStore()).isTrue();
+ span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ span.end();
+ Mockito.verify(startEndHandler, Mockito.times(1)).onEnd(span);
+ assertThat(span.getSampleToLocalSpanStore()).isFalse();
+ }
+
+ @Test
+ public void sampleToLocalSpanStore_RunningSpan() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ exception.expect(IllegalStateException.class);
+ exception.expectMessage("Running span does not have the SampleToLocalSpanStore set.");
+ span.getSampleToLocalSpanStore();
+ }
+
+ @Test
+ public void getSpanKind() {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ Kind.SERVER,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ assertThat(span.getKind()).isEqualTo(Kind.SERVER);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/SpanBuilderImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/SpanBuilderImplTest.java
new file mode 100644
index 00000000..3267eac5
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/SpanBuilderImplTest.java
@@ -0,0 +1,404 @@
+/*
+ * 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.trace;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.implcore.trace.internal.RandomHandler;
+import io.opencensus.testing.common.TestClock;
+import io.opencensus.trace.Span;
+import io.opencensus.trace.Span.Kind;
+import io.opencensus.trace.Span.Options;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceConfig;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SpanData;
+import io.opencensus.trace.samplers.Samplers;
+import java.util.Collections;
+import java.util.Random;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link SpanBuilderImpl}. */
+@RunWith(JUnit4.class)
+public class SpanBuilderImplTest {
+ private static final String SPAN_NAME = "MySpanName";
+ private SpanBuilderImpl.Options spanBuilderOptions;
+ private final TraceParams alwaysSampleTraceParams =
+ TraceParams.DEFAULT.toBuilder().setSampler(Samplers.alwaysSample()).build();
+ private final TestClock testClock = TestClock.create();
+ private final RandomHandler randomHandler = new FakeRandomHandler();
+ @Mock private StartEndHandler startEndHandler;
+ @Mock private TraceConfig traceConfig;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ spanBuilderOptions =
+ new SpanBuilderImpl.Options(randomHandler, startEndHandler, testClock, traceConfig);
+ when(traceConfig.getActiveTraceParams()).thenReturn(alwaysSampleTraceParams);
+ }
+
+ @Test
+ public void startSpan_CreatesTheCorrectSpanImplInstance() {
+ assertThat(
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.alwaysSample())
+ .startSpan())
+ .isInstanceOf(RecordEventsSpanImpl.class);
+ assertThat(
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setRecordEvents(true)
+ .setSampler(Samplers.neverSample())
+ .startSpan())
+ .isInstanceOf(RecordEventsSpanImpl.class);
+ assertThat(
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan())
+ .isInstanceOf(NoRecordEventsSpanImpl.class);
+ }
+
+ @Test
+ public void setSpanKind_NotNull() {
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSpanKind(Kind.CLIENT)
+ .setRecordEvents(true)
+ .startSpan();
+ assertThat(span.getKind()).isEqualTo(Kind.CLIENT);
+ assertThat(span.toSpanData().getKind()).isEqualTo(Kind.CLIENT);
+ }
+
+ @Test
+ public void setSpanKind_DefaultNull() {
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setRecordEvents(true)
+ .startSpan();
+ assertThat(span.getKind()).isNull();
+ assertThat(span.toSpanData().getKind()).isNull();
+ }
+
+ @Test
+ public void startSpanNullParent() {
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setRecordEvents(true)
+ .startSpan();
+ assertThat(span.getContext().isValid()).isTrue();
+ assertThat(span.getOptions().contains(Options.RECORD_EVENTS)).isTrue();
+ assertThat(span.getContext().getTraceOptions().isSampled()).isTrue();
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getParentSpanId()).isNull();
+ assertThat(spanData.getHasRemoteParent()).isNull();
+ assertThat(spanData.getStartTimestamp()).isEqualTo(testClock.now());
+ assertThat(spanData.getName()).isEqualTo(SPAN_NAME);
+ }
+
+ @Test
+ public void startSpanNullParentWithRecordEvents() {
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .setRecordEvents(true)
+ .startSpan();
+ assertThat(span.getContext().isValid()).isTrue();
+ assertThat(span.getOptions().contains(Options.RECORD_EVENTS)).isTrue();
+ assertThat(span.getContext().getTraceOptions().isSampled()).isFalse();
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getParentSpanId()).isNull();
+ assertThat(spanData.getHasRemoteParent()).isNull();
+ }
+
+ @Test
+ public void startSpanNullParentNoRecordOptions() {
+ Span span =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(span.getContext().isValid()).isTrue();
+ assertThat(span.getOptions().contains(Options.RECORD_EVENTS)).isFalse();
+ assertThat(span.getContext().getTraceOptions().isSampled()).isFalse();
+ }
+
+ @Test
+ public void startChildSpan() {
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions).startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getOptions().contains(Options.RECORD_EVENTS)).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ assertThat(((RecordEventsSpanImpl) rootSpan).toSpanData().getHasRemoteParent()).isNull();
+ Span childSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, rootSpan, spanBuilderOptions).startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(rootSpan.getContext().getTraceId());
+ assertThat(((RecordEventsSpanImpl) childSpan).toSpanData().getParentSpanId())
+ .isEqualTo(rootSpan.getContext().getSpanId());
+ assertThat(((RecordEventsSpanImpl) childSpan).toSpanData().getHasRemoteParent()).isFalse();
+ assertThat(((RecordEventsSpanImpl) childSpan).getTimestampConverter())
+ .isEqualTo(((RecordEventsSpanImpl) rootSpan).getTimestampConverter());
+ }
+
+ @Test
+ public void startRemoteSpan_NullParent() {
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithRemoteParent(SPAN_NAME, null, spanBuilderOptions)
+ .setRecordEvents(true)
+ .startSpan();
+ assertThat(span.getContext().isValid()).isTrue();
+ assertThat(span.getOptions().contains(Options.RECORD_EVENTS)).isTrue();
+ assertThat(span.getContext().getTraceOptions().isSampled()).isTrue();
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getParentSpanId()).isNull();
+ assertThat(spanData.getHasRemoteParent()).isNull();
+ }
+
+ @Test
+ public void startRemoteSpanInvalidParent() {
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithRemoteParent(
+ SPAN_NAME, SpanContext.INVALID, spanBuilderOptions)
+ .startSpan();
+ assertThat(span.getContext().isValid()).isTrue();
+ assertThat(span.getOptions().contains(Options.RECORD_EVENTS)).isTrue();
+ assertThat(span.getContext().getTraceOptions().isSampled()).isTrue();
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getParentSpanId()).isNull();
+ assertThat(spanData.getHasRemoteParent()).isNull();
+ }
+
+ @Test
+ public void startRemoteSpan() {
+ SpanContext spanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(randomHandler.current()),
+ SpanId.generateRandomId(randomHandler.current()),
+ TraceOptions.DEFAULT);
+ RecordEventsSpanImpl span =
+ (RecordEventsSpanImpl)
+ SpanBuilderImpl.createWithRemoteParent(SPAN_NAME, spanContext, spanBuilderOptions)
+ .setRecordEvents(true)
+ .startSpan();
+ assertThat(span.getContext().isValid()).isTrue();
+ assertThat(span.getContext().getTraceId()).isEqualTo(spanContext.getTraceId());
+ assertThat(span.getContext().getTraceOptions().isSampled()).isTrue();
+ SpanData spanData = span.toSpanData();
+ assertThat(spanData.getParentSpanId()).isEqualTo(spanContext.getSpanId());
+ assertThat(spanData.getHasRemoteParent()).isTrue();
+ }
+
+ @Test
+ public void startRootSpan_WithSpecifiedSampler() {
+ // Apply given sampler before default sampler for root spans.
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ }
+
+ @Test
+ public void startRootSpan_WithoutSpecifiedSampler() {
+ // Apply default sampler (always true in the tests) for root spans.
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions).startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ }
+
+ @Test
+ public void startRemoteChildSpan_WithSpecifiedSampler() {
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.alwaysSample())
+ .startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ // Apply given sampler before default sampler for spans with remote parent.
+ Span childSpan =
+ SpanBuilderImpl.createWithRemoteParent(SPAN_NAME, rootSpan.getContext(), spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(rootSpan.getContext().getTraceId());
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ }
+
+ @Test
+ public void startRemoteChildSpan_WithoutSpecifiedSampler() {
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ // Apply default sampler (always true in the tests) for spans with remote parent.
+ Span childSpan =
+ SpanBuilderImpl.createWithRemoteParent(SPAN_NAME, rootSpan.getContext(), spanBuilderOptions)
+ .startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(rootSpan.getContext().getTraceId());
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ }
+
+ @Test
+ public void startChildSpan_WithSpecifiedSampler() {
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.alwaysSample())
+ .startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ // Apply the given sampler for child spans.
+ Span childSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, rootSpan, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(rootSpan.getContext().getTraceId());
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ }
+
+ @Test
+ public void startChildSpan_WithoutSpecifiedSampler() {
+ Span rootSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(rootSpan.getContext().isValid()).isTrue();
+ assertThat(rootSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ // Don't apply the default sampler (always true) for child spans.
+ Span childSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, rootSpan, spanBuilderOptions).startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(rootSpan.getContext().getTraceId());
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ }
+
+ @Test
+ public void startChildSpan_SampledLinkedParent() {
+ Span rootSpanUnsampled =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.neverSample())
+ .startSpan();
+ assertThat(rootSpanUnsampled.getContext().getTraceOptions().isSampled()).isFalse();
+ Span rootSpanSampled =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, null, spanBuilderOptions)
+ .setSampler(Samplers.alwaysSample())
+ .startSpan();
+ assertThat(rootSpanSampled.getContext().getTraceOptions().isSampled()).isTrue();
+ // Sampled because the linked parent is sampled.
+ Span childSpan =
+ SpanBuilderImpl.createWithParent(SPAN_NAME, rootSpanUnsampled, spanBuilderOptions)
+ .setParentLinks(Collections.singletonList(rootSpanSampled))
+ .startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId())
+ .isEqualTo(rootSpanUnsampled.getContext().getTraceId());
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ }
+
+ @Test
+ public void startRemoteChildSpan_WithProbabilitySamplerDefaultSampler() {
+ when(traceConfig.getActiveTraceParams()).thenReturn(TraceParams.DEFAULT);
+ // This traceId will not be sampled by the ProbabilitySampler because the first 8 bytes as long
+ // is not less than probability * Long.MAX_VALUE;
+ TraceId traceId =
+ TraceId.fromBytes(
+ new byte[] {
+ (byte) 0x8F,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ });
+
+ // If parent is sampled then the remote child must be sampled.
+ Span childSpan =
+ SpanBuilderImpl.createWithRemoteParent(
+ SPAN_NAME,
+ SpanContext.create(
+ traceId,
+ SpanId.generateRandomId(randomHandler.current()),
+ TraceOptions.builder().setIsSampled(true).build()),
+ spanBuilderOptions)
+ .startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(traceId);
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isTrue();
+ childSpan.end();
+
+ assertThat(traceConfig.getActiveTraceParams()).isEqualTo(TraceParams.DEFAULT);
+
+ // If parent is not sampled then the remote child must be not sampled.
+ childSpan =
+ SpanBuilderImpl.createWithRemoteParent(
+ SPAN_NAME,
+ SpanContext.create(
+ traceId,
+ SpanId.generateRandomId(randomHandler.current()),
+ TraceOptions.DEFAULT),
+ spanBuilderOptions)
+ .startSpan();
+ assertThat(childSpan.getContext().isValid()).isTrue();
+ assertThat(childSpan.getContext().getTraceId()).isEqualTo(traceId);
+ assertThat(childSpan.getContext().getTraceOptions().isSampled()).isFalse();
+ childSpan.end();
+ }
+
+ private static final class FakeRandomHandler extends RandomHandler {
+ private final Random random;
+
+ FakeRandomHandler() {
+ this.random = new Random(1234);
+ }
+
+ @Override
+ public Random current() {
+ return random;
+ }
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/TraceComponentImplBaseTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/TraceComponentImplBaseTest.java
new file mode 100644
index 00000000..9f468442
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/TraceComponentImplBaseTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.trace;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.implcore.common.MillisClock;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.trace.export.ExportComponentImpl;
+import io.opencensus.implcore.trace.internal.RandomHandler.SecureRandomHandler;
+import io.opencensus.implcore.trace.propagation.PropagationComponentImpl;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link TraceComponentImplBase}. */
+@RunWith(JUnit4.class)
+public class TraceComponentImplBaseTest {
+ private final TraceComponentImplBase traceComponentImplBase =
+ new TraceComponentImplBase(
+ MillisClock.getInstance(), new SecureRandomHandler(), new SimpleEventQueue());
+
+ @Test
+ public void implementationOfTracer() {
+ assertThat(traceComponentImplBase.getTracer()).isInstanceOf(TracerImpl.class);
+ }
+
+ @Test
+ public void implementationOfBinaryPropagationHandler() {
+ assertThat(traceComponentImplBase.getPropagationComponent())
+ .isInstanceOf(PropagationComponentImpl.class);
+ }
+
+ @Test
+ public void implementationOfClock() {
+ assertThat(traceComponentImplBase.getClock()).isInstanceOf(MillisClock.class);
+ }
+
+ @Test
+ public void implementationOfTraceExporter() {
+ assertThat(traceComponentImplBase.getExportComponent()).isInstanceOf(ExportComponentImpl.class);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/TracerImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/TracerImplTest.java
new file mode 100644
index 00000000..d10be6a2
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/TracerImplTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.trace;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.implcore.trace.internal.RandomHandler.SecureRandomHandler;
+import io.opencensus.testing.common.TestClock;
+import io.opencensus.trace.BlankSpan;
+import io.opencensus.trace.SpanBuilder;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.config.TraceConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link TracerImpl}. */
+@RunWith(JUnit4.class)
+public class TracerImplTest {
+ private static final String SPAN_NAME = "MySpanName";
+ @Mock private StartEndHandler startEndHandler;
+ @Mock private TraceConfig traceConfig;
+ private TracerImpl tracer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ tracer =
+ new TracerImpl(new SecureRandomHandler(), startEndHandler, TestClock.create(), traceConfig);
+ }
+
+ @Test
+ public void createSpanBuilder() {
+ SpanBuilder spanBuilder = tracer.spanBuilderWithExplicitParent(SPAN_NAME, BlankSpan.INSTANCE);
+ assertThat(spanBuilder).isInstanceOf(SpanBuilderImpl.class);
+ }
+
+ @Test
+ public void createSpanBuilderWithRemoteParet() {
+ SpanBuilder spanBuilder = tracer.spanBuilderWithRemoteParent(SPAN_NAME, SpanContext.INVALID);
+ assertThat(spanBuilder).isInstanceOf(SpanBuilderImpl.class);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/config/TraceConfigImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/config/TraceConfigImplTest.java
new file mode 100644
index 00000000..ecaeda6d
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/config/TraceConfigImplTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.trace.config;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.samplers.Samplers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link TraceConfigImpl}. */
+@RunWith(JUnit4.class)
+public class TraceConfigImplTest {
+ private final TraceConfigImpl traceConfig = new TraceConfigImpl();
+
+ @Test
+ public void defaultActiveTraceParams() {
+ assertThat(traceConfig.getActiveTraceParams()).isEqualTo(TraceParams.DEFAULT);
+ }
+
+ @Test
+ public void updateTraceParams() {
+ TraceParams traceParams =
+ TraceParams.DEFAULT
+ .toBuilder()
+ .setSampler(Samplers.alwaysSample())
+ .setMaxNumberOfAttributes(8)
+ .setMaxNumberOfAnnotations(9)
+ .setMaxNumberOfNetworkEvents(10)
+ .setMaxNumberOfLinks(11)
+ .build();
+ traceConfig.updateActiveTraceParams(traceParams);
+ assertThat(traceConfig.getActiveTraceParams()).isEqualTo(traceParams);
+ traceConfig.updateActiveTraceParams(TraceParams.DEFAULT);
+ assertThat(traceConfig.getActiveTraceParams()).isEqualTo(TraceParams.DEFAULT);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/export/ExportComponentImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/export/ExportComponentImplTest.java
new file mode 100644
index 00000000..4b8993ff
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/export/ExportComponentImplTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.trace.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.trace.export.ExportComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link ExportComponentImpl}. */
+@RunWith(JUnit4.class)
+public class ExportComponentImplTest {
+ private final ExportComponent exportComponentWithInProcess =
+ ExportComponentImpl.createWithInProcessStores(new SimpleEventQueue());
+ private final ExportComponent exportComponentWithoutInProcess =
+ ExportComponentImpl.createWithoutInProcessStores(new SimpleEventQueue());
+
+ @Test
+ public void implementationOfSpanExporter() {
+ assertThat(exportComponentWithInProcess.getSpanExporter()).isInstanceOf(SpanExporterImpl.class);
+ }
+
+ @Test
+ public void implementationOfActiveSpans() {
+ assertThat(exportComponentWithInProcess.getRunningSpanStore())
+ .isInstanceOf(InProcessRunningSpanStoreImpl.class);
+ assertThat(exportComponentWithoutInProcess.getRunningSpanStore())
+ .isInstanceOf(RunningSpanStoreImpl.getNoopRunningSpanStoreImpl().getClass());
+ }
+
+ @Test
+ public void implementationOfSampledSpanStore() {
+ assertThat(exportComponentWithInProcess.getSampledSpanStore())
+ .isInstanceOf(InProcessSampledSpanStoreImpl.class);
+ assertThat(exportComponentWithoutInProcess.getSampledSpanStore())
+ .isInstanceOf(SampledSpanStoreImpl.getNoopSampledSpanStoreImpl().getClass());
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessRunningSpanStoreImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessRunningSpanStoreImplTest.java
new file mode 100644
index 00000000..68ce1c18
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessRunningSpanStoreImplTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.trace.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Duration;
+import io.opencensus.implcore.common.MillisClock;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.implcore.trace.StartEndHandlerImpl;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.RunningSpanStore.Filter;
+import java.util.Random;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link InProcessRunningSpanStoreImpl}. */
+@RunWith(JUnit4.class)
+public class InProcessRunningSpanStoreImplTest {
+
+ private static final String SPAN_NAME_1 = "MySpanName/1";
+ private static final String SPAN_NAME_2 = "MySpanName/2";
+ private final Random random = new Random(1234);
+ private final SpanExporterImpl sampledSpansServiceExporter =
+ SpanExporterImpl.create(4, Duration.create(1, 0));
+ private final InProcessRunningSpanStoreImpl activeSpansExporter =
+ new InProcessRunningSpanStoreImpl();
+ private final StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(
+ sampledSpansServiceExporter, activeSpansExporter, null, new SimpleEventQueue());
+
+ private RecordEventsSpanImpl createSpan(String spanName) {
+ final SpanContext spanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random),
+ SpanId.generateRandomId(random),
+ TraceOptions.DEFAULT);
+ return RecordEventsSpanImpl.startSpan(
+ spanContext,
+ spanName,
+ null,
+ SpanId.generateRandomId(random),
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ null,
+ MillisClock.getInstance());
+ }
+
+ @Test
+ public void getSummary_SpansWithDifferentNames() {
+ final RecordEventsSpanImpl span1 = createSpan(SPAN_NAME_1);
+ final RecordEventsSpanImpl span2 = createSpan(SPAN_NAME_2);
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(2);
+ assertThat(
+ activeSpansExporter
+ .getSummary()
+ .getPerSpanNameSummary()
+ .get(SPAN_NAME_1)
+ .getNumRunningSpans())
+ .isEqualTo(1);
+ assertThat(
+ activeSpansExporter
+ .getSummary()
+ .getPerSpanNameSummary()
+ .get(SPAN_NAME_2)
+ .getNumRunningSpans())
+ .isEqualTo(1);
+ span1.end();
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(1);
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().get(SPAN_NAME_1)).isNull();
+ assertThat(
+ activeSpansExporter
+ .getSummary()
+ .getPerSpanNameSummary()
+ .get(SPAN_NAME_2)
+ .getNumRunningSpans())
+ .isEqualTo(1);
+ span2.end();
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void getSummary_SpansWithSameName() {
+ final RecordEventsSpanImpl span1 = createSpan(SPAN_NAME_1);
+ final RecordEventsSpanImpl span2 = createSpan(SPAN_NAME_1);
+ final RecordEventsSpanImpl span3 = createSpan(SPAN_NAME_1);
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(1);
+ assertThat(
+ activeSpansExporter
+ .getSummary()
+ .getPerSpanNameSummary()
+ .get(SPAN_NAME_1)
+ .getNumRunningSpans())
+ .isEqualTo(3);
+ span1.end();
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(1);
+ assertThat(
+ activeSpansExporter
+ .getSummary()
+ .getPerSpanNameSummary()
+ .get(SPAN_NAME_1)
+ .getNumRunningSpans())
+ .isEqualTo(2);
+ span2.end();
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(1);
+ assertThat(
+ activeSpansExporter
+ .getSummary()
+ .getPerSpanNameSummary()
+ .get(SPAN_NAME_1)
+ .getNumRunningSpans())
+ .isEqualTo(1);
+ span3.end();
+ assertThat(activeSpansExporter.getSummary().getPerSpanNameSummary().size()).isEqualTo(0);
+ }
+
+ @Test
+ public void getActiveSpans_SpansWithDifferentNames() {
+ RecordEventsSpanImpl span1 = createSpan(SPAN_NAME_1);
+ RecordEventsSpanImpl span2 = createSpan(SPAN_NAME_2);
+ assertThat(activeSpansExporter.getRunningSpans(Filter.create(SPAN_NAME_1, 0)))
+ .containsExactly(span1.toSpanData());
+ assertThat(activeSpansExporter.getRunningSpans(Filter.create(SPAN_NAME_1, 2)))
+ .containsExactly(span1.toSpanData());
+ assertThat(activeSpansExporter.getRunningSpans(Filter.create(SPAN_NAME_2, 0)))
+ .containsExactly(span2.toSpanData());
+ span1.end();
+ span2.end();
+ }
+
+ @Test
+ public void getActiveSpans_SpansWithSameName() {
+ RecordEventsSpanImpl span1 = createSpan(SPAN_NAME_1);
+ RecordEventsSpanImpl span2 = createSpan(SPAN_NAME_1);
+ RecordEventsSpanImpl span3 = createSpan(SPAN_NAME_1);
+ assertThat(activeSpansExporter.getRunningSpans(Filter.create(SPAN_NAME_1, 0)))
+ .containsExactly(span1.toSpanData(), span2.toSpanData(), span3.toSpanData());
+ assertThat(activeSpansExporter.getRunningSpans(Filter.create(SPAN_NAME_1, 2)).size())
+ .isEqualTo(2);
+ assertThat(activeSpansExporter.getRunningSpans(Filter.create(SPAN_NAME_1, 2)))
+ .containsAnyOf(span1.toSpanData(), span2.toSpanData(), span3.toSpanData());
+ span1.end();
+ span2.end();
+ span3.end();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessSampledSpanStoreImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessSampledSpanStoreImplTest.java
new file mode 100644
index 00000000..7d8b434e
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/export/InProcessSampledSpanStoreImplTest.java
@@ -0,0 +1,368 @@
+/*
+ * 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.trace.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Duration;
+import io.opencensus.common.Timestamp;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.testing.common.TestClock;
+import io.opencensus.trace.EndSpanOptions;
+import io.opencensus.trace.Span;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.Status;
+import io.opencensus.trace.Status.CanonicalCode;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SampledSpanStore.ErrorFilter;
+import io.opencensus.trace.export.SampledSpanStore.LatencyBucketBoundaries;
+import io.opencensus.trace.export.SampledSpanStore.LatencyFilter;
+import io.opencensus.trace.export.SampledSpanStore.PerSpanNameSummary;
+import io.opencensus.trace.export.SpanData;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link InProcessSampledSpanStoreImpl}. */
+@RunWith(JUnit4.class)
+public class InProcessSampledSpanStoreImplTest {
+ private static final String REGISTERED_SPAN_NAME = "MySpanName/1";
+ private static final String NOT_REGISTERED_SPAN_NAME = "MySpanName/2";
+ private static final long NUM_NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1);
+ private final Random random = new Random(1234);
+ private final SpanContext sampledSpanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random),
+ SpanId.generateRandomId(random),
+ TraceOptions.builder().setIsSampled(true).build());
+ private final SpanContext notSampledSpanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random), SpanId.generateRandomId(random), TraceOptions.DEFAULT);
+ private final SpanId parentSpanId = SpanId.generateRandomId(random);
+ private final TestClock testClock = TestClock.create(Timestamp.create(12345, 54321));
+ private final InProcessSampledSpanStoreImpl sampleStore =
+ new InProcessSampledSpanStoreImpl(new SimpleEventQueue());
+ private final StartEndHandler startEndHandler =
+ new StartEndHandler() {
+ @Override
+ public void onStart(RecordEventsSpanImpl span) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onEnd(RecordEventsSpanImpl span) {
+ sampleStore.considerForSampling(span);
+ }
+ };
+
+ @Before
+ public void setUp() {
+ sampleStore.registerSpanNamesForCollection(Collections.singletonList(REGISTERED_SPAN_NAME));
+ }
+
+ private RecordEventsSpanImpl createSampledSpan(String spanName) {
+ return RecordEventsSpanImpl.startSpan(
+ sampledSpanContext,
+ spanName,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ null,
+ testClock);
+ }
+
+ private RecordEventsSpanImpl createNotSampledSpan(String spanName) {
+ return RecordEventsSpanImpl.startSpan(
+ notSampledSpanContext,
+ spanName,
+ null,
+ parentSpanId,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ null,
+ testClock);
+ }
+
+ private void addSpanNameToAllLatencyBuckets(String spanName) {
+ for (LatencyBucketBoundaries boundaries : LatencyBucketBoundaries.values()) {
+ Span sampledSpan = createSampledSpan(spanName);
+ Span notSampledSpan = createNotSampledSpan(spanName);
+ if (boundaries.getLatencyLowerNs() < NUM_NANOS_PER_SECOND) {
+ testClock.advanceTime(Duration.create(0, (int) boundaries.getLatencyLowerNs()));
+ } else {
+ testClock.advanceTime(
+ Duration.create(
+ boundaries.getLatencyLowerNs() / NUM_NANOS_PER_SECOND,
+ (int) (boundaries.getLatencyLowerNs() % NUM_NANOS_PER_SECOND)));
+ }
+ sampledSpan.end();
+ notSampledSpan.end();
+ }
+ }
+
+ private void addSpanNameToAllErrorBuckets(String spanName) {
+ for (CanonicalCode code : CanonicalCode.values()) {
+ if (code != CanonicalCode.OK) {
+ Span sampledSpan = createSampledSpan(spanName);
+ Span notSampledSpan = createNotSampledSpan(spanName);
+ testClock.advanceTime(Duration.create(0, 1000));
+ sampledSpan.end(EndSpanOptions.builder().setStatus(code.toStatus()).build());
+ notSampledSpan.end(EndSpanOptions.builder().setStatus(code.toStatus()).build());
+ }
+ }
+ }
+
+ @Test
+ public void addSpansWithRegisteredNamesInAllLatencyBuckets() {
+ addSpanNameToAllLatencyBuckets(REGISTERED_SPAN_NAME);
+ Map<String, PerSpanNameSummary> perSpanNameSummary =
+ sampleStore.getSummary().getPerSpanNameSummary();
+ assertThat(perSpanNameSummary.size()).isEqualTo(1);
+ Map<LatencyBucketBoundaries, Integer> latencyBucketsSummaries =
+ perSpanNameSummary.get(REGISTERED_SPAN_NAME).getNumbersOfLatencySampledSpans();
+ assertThat(latencyBucketsSummaries.size()).isEqualTo(LatencyBucketBoundaries.values().length);
+ for (Map.Entry<LatencyBucketBoundaries, Integer> it : latencyBucketsSummaries.entrySet()) {
+ assertThat(it.getValue()).isEqualTo(2);
+ }
+ }
+
+ @Test
+ public void addSpansWithoutRegisteredNamesInAllLatencyBuckets() {
+ addSpanNameToAllLatencyBuckets(NOT_REGISTERED_SPAN_NAME);
+ Map<String, PerSpanNameSummary> perSpanNameSummary =
+ sampleStore.getSummary().getPerSpanNameSummary();
+ assertThat(perSpanNameSummary.size()).isEqualTo(1);
+ assertThat(perSpanNameSummary.containsKey(NOT_REGISTERED_SPAN_NAME)).isFalse();
+ }
+
+ @Test
+ public void registerUnregisterAndListSpanNames() {
+ assertThat(sampleStore.getRegisteredSpanNamesForCollection())
+ .containsExactly(REGISTERED_SPAN_NAME);
+ sampleStore.registerSpanNamesForCollection(Collections.singletonList(NOT_REGISTERED_SPAN_NAME));
+ assertThat(sampleStore.getRegisteredSpanNamesForCollection())
+ .containsExactly(REGISTERED_SPAN_NAME, NOT_REGISTERED_SPAN_NAME);
+ sampleStore.unregisterSpanNamesForCollection(
+ Collections.singletonList(NOT_REGISTERED_SPAN_NAME));
+ assertThat(sampleStore.getRegisteredSpanNamesForCollection())
+ .containsExactly(REGISTERED_SPAN_NAME);
+ }
+
+ @Test
+ public void registerSpanNamesViaSpanBuilderOption() {
+ assertThat(sampleStore.getRegisteredSpanNamesForCollection())
+ .containsExactly(REGISTERED_SPAN_NAME);
+ createSampledSpan(NOT_REGISTERED_SPAN_NAME)
+ .end(EndSpanOptions.builder().setSampleToLocalSpanStore(true).build());
+ assertThat(sampleStore.getRegisteredSpanNamesForCollection())
+ .containsExactly(REGISTERED_SPAN_NAME, NOT_REGISTERED_SPAN_NAME);
+ }
+
+ @Test
+ public void addSpansWithRegisteredNamesInAllErrorBuckets() {
+ addSpanNameToAllErrorBuckets(REGISTERED_SPAN_NAME);
+ Map<String, PerSpanNameSummary> perSpanNameSummary =
+ sampleStore.getSummary().getPerSpanNameSummary();
+ assertThat(perSpanNameSummary.size()).isEqualTo(1);
+ Map<CanonicalCode, Integer> errorBucketsSummaries =
+ perSpanNameSummary.get(REGISTERED_SPAN_NAME).getNumbersOfErrorSampledSpans();
+ assertThat(errorBucketsSummaries.size()).isEqualTo(CanonicalCode.values().length - 1);
+ for (Map.Entry<CanonicalCode, Integer> it : errorBucketsSummaries.entrySet()) {
+ assertThat(it.getValue()).isEqualTo(2);
+ }
+ }
+
+ @Test
+ public void addSpansWithoutRegisteredNamesInAllErrorBuckets() {
+ addSpanNameToAllErrorBuckets(NOT_REGISTERED_SPAN_NAME);
+ Map<String, PerSpanNameSummary> perSpanNameSummary =
+ sampleStore.getSummary().getPerSpanNameSummary();
+ assertThat(perSpanNameSummary.size()).isEqualTo(1);
+ assertThat(perSpanNameSummary.containsKey(NOT_REGISTERED_SPAN_NAME)).isFalse();
+ }
+
+ @Test
+ public void getErrorSampledSpans() {
+ RecordEventsSpanImpl span = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span.end(EndSpanOptions.builder().setStatus(Status.CANCELLED).build());
+ Collection<SpanData> samples =
+ sampleStore.getErrorSampledSpans(
+ ErrorFilter.create(REGISTERED_SPAN_NAME, CanonicalCode.CANCELLED, 0));
+ assertThat(samples.size()).isEqualTo(1);
+ assertThat(samples.contains(span.toSpanData())).isTrue();
+ }
+
+ @Test
+ public void getErrorSampledSpans_MaxSpansToReturn() {
+ RecordEventsSpanImpl span1 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span1.end(EndSpanOptions.builder().setStatus(Status.CANCELLED).build());
+ // Advance time to allow other spans to be sampled.
+ testClock.advanceTime(Duration.create(5, 0));
+ RecordEventsSpanImpl span2 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span2.end(EndSpanOptions.builder().setStatus(Status.CANCELLED).build());
+ Collection<SpanData> samples =
+ sampleStore.getErrorSampledSpans(
+ ErrorFilter.create(REGISTERED_SPAN_NAME, CanonicalCode.CANCELLED, 1));
+ assertThat(samples.size()).isEqualTo(1);
+ // No order guaranteed so one of the spans should be in the list.
+ assertThat(samples).containsAnyOf(span1.toSpanData(), span2.toSpanData());
+ }
+
+ @Test
+ public void getErrorSampledSpans_NullCode() {
+ RecordEventsSpanImpl span1 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span1.end(EndSpanOptions.builder().setStatus(Status.CANCELLED).build());
+ RecordEventsSpanImpl span2 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span2.end(EndSpanOptions.builder().setStatus(Status.UNKNOWN).build());
+ Collection<SpanData> samples =
+ sampleStore.getErrorSampledSpans(ErrorFilter.create(REGISTERED_SPAN_NAME, null, 0));
+ assertThat(samples.size()).isEqualTo(2);
+ assertThat(samples).containsExactly(span1.toSpanData(), span2.toSpanData());
+ }
+
+ @Test
+ public void getErrorSampledSpans_NullCode_MaxSpansToReturn() {
+ RecordEventsSpanImpl span1 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span1.end(EndSpanOptions.builder().setStatus(Status.CANCELLED).build());
+ RecordEventsSpanImpl span2 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, 1000));
+ span2.end(EndSpanOptions.builder().setStatus(Status.UNKNOWN).build());
+ Collection<SpanData> samples =
+ sampleStore.getErrorSampledSpans(ErrorFilter.create(REGISTERED_SPAN_NAME, null, 1));
+ assertThat(samples.size()).isEqualTo(1);
+ assertThat(samples).containsAnyOf(span1.toSpanData(), span2.toSpanData());
+ }
+
+ @Test
+ public void getLatencySampledSpans() {
+ RecordEventsSpanImpl span = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(20)));
+ span.end();
+ Collection<SpanData> samples =
+ sampleStore.getLatencySampledSpans(
+ LatencyFilter.create(
+ REGISTERED_SPAN_NAME,
+ TimeUnit.MICROSECONDS.toNanos(15),
+ TimeUnit.MICROSECONDS.toNanos(25),
+ 0));
+ assertThat(samples.size()).isEqualTo(1);
+ assertThat(samples.contains(span.toSpanData())).isTrue();
+ }
+
+ @Test
+ public void getLatencySampledSpans_ExclusiveUpperBound() {
+ RecordEventsSpanImpl span = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(20)));
+ span.end();
+ Collection<SpanData> samples =
+ sampleStore.getLatencySampledSpans(
+ LatencyFilter.create(
+ REGISTERED_SPAN_NAME,
+ TimeUnit.MICROSECONDS.toNanos(15),
+ TimeUnit.MICROSECONDS.toNanos(20),
+ 0));
+ assertThat(samples.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void getLatencySampledSpans_InclusiveLowerBound() {
+ RecordEventsSpanImpl span = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(20)));
+ span.end();
+ Collection<SpanData> samples =
+ sampleStore.getLatencySampledSpans(
+ LatencyFilter.create(
+ REGISTERED_SPAN_NAME,
+ TimeUnit.MICROSECONDS.toNanos(20),
+ TimeUnit.MICROSECONDS.toNanos(25),
+ 0));
+ assertThat(samples.size()).isEqualTo(1);
+ assertThat(samples.contains(span.toSpanData())).isTrue();
+ }
+
+ @Test
+ public void getLatencySampledSpans_QueryBetweenMultipleBuckets() {
+ RecordEventsSpanImpl span1 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(20)));
+ span1.end();
+ // Advance time to allow other spans to be sampled.
+ testClock.advanceTime(Duration.create(5, 0));
+ RecordEventsSpanImpl span2 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(200)));
+ span2.end();
+ Collection<SpanData> samples =
+ sampleStore.getLatencySampledSpans(
+ LatencyFilter.create(
+ REGISTERED_SPAN_NAME,
+ TimeUnit.MICROSECONDS.toNanos(15),
+ TimeUnit.MICROSECONDS.toNanos(250),
+ 0));
+ assertThat(samples).containsExactly(span1.toSpanData(), span2.toSpanData());
+ }
+
+ @Test
+ public void getLatencySampledSpans_MaxSpansToReturn() {
+ RecordEventsSpanImpl span1 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(20)));
+ span1.end();
+ // Advance time to allow other spans to be sampled.
+ testClock.advanceTime(Duration.create(5, 0));
+ RecordEventsSpanImpl span2 = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(200)));
+ span2.end();
+ Collection<SpanData> samples =
+ sampleStore.getLatencySampledSpans(
+ LatencyFilter.create(
+ REGISTERED_SPAN_NAME,
+ TimeUnit.MICROSECONDS.toNanos(15),
+ TimeUnit.MICROSECONDS.toNanos(250),
+ 1));
+ assertThat(samples.size()).isEqualTo(1);
+ assertThat(samples.contains(span1.toSpanData())).isTrue();
+ }
+
+ @Test
+ public void ignoreNegativeSpanLatency() {
+ RecordEventsSpanImpl span = createSampledSpan(REGISTERED_SPAN_NAME);
+ testClock.advanceTime(Duration.create(0, (int) TimeUnit.MICROSECONDS.toNanos(-20)));
+ span.end();
+ Collection<SpanData> samples =
+ sampleStore.getLatencySampledSpans(
+ LatencyFilter.create(REGISTERED_SPAN_NAME, 0, Long.MAX_VALUE, 0));
+ assertThat(samples.size()).isEqualTo(0);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopRunningSpanStoreImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopRunningSpanStoreImplTest.java
new file mode 100644
index 00000000..96669df7
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopRunningSpanStoreImplTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018, 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.trace.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Timestamp;
+import io.opencensus.implcore.internal.EventQueue;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.internal.TimestampConverter;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.testing.common.TestClock;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.RunningSpanStore.Filter;
+import java.util.Random;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link RunningSpanStoreImpl.NoopRunningSpanStoreImpl}. */
+@RunWith(JUnit4.class)
+public class NoopRunningSpanStoreImplTest {
+
+ private static final String SPAN_NAME = "MySpanName";
+
+ private final Timestamp timestamp = Timestamp.create(1234, 5678);
+ private final Random random = new Random(1234);
+ private final SpanContext spanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random), SpanId.generateRandomId(random), TraceOptions.DEFAULT);
+ private final TestClock testClock = TestClock.create(timestamp);
+ private final TimestampConverter timestampConverter = TimestampConverter.now(testClock);
+ @Mock private StartEndHandler startEndHandler;
+ private RecordEventsSpanImpl recordEventsSpanImpl;
+ // maxSpansToReturn=0 means all
+ private final Filter filter = Filter.create(SPAN_NAME, 0 /* maxSpansToReturn */);
+ private final EventQueue eventQueue = new SimpleEventQueue();
+ private final RunningSpanStoreImpl runningSpanStoreImpl =
+ ExportComponentImpl.createWithoutInProcessStores(eventQueue).getRunningSpanStore();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ recordEventsSpanImpl =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ null,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ }
+
+ private void getMethodsShouldReturnEmpty() {
+ // get methods should always return empty collections.
+ assertThat(runningSpanStoreImpl.getSummary().getPerSpanNameSummary()).isEmpty();
+ assertThat(runningSpanStoreImpl.getRunningSpans(filter)).isEmpty();
+ }
+
+ @Test
+ public void noopImplementation() {
+ getMethodsShouldReturnEmpty();
+ // onStart() does not affect the result.
+ runningSpanStoreImpl.onStart(recordEventsSpanImpl);
+ getMethodsShouldReturnEmpty();
+ // onEnd() does not affect the result.
+ runningSpanStoreImpl.onEnd(recordEventsSpanImpl);
+ getMethodsShouldReturnEmpty();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopSampledSpanStoreImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopSampledSpanStoreImplTest.java
new file mode 100644
index 00000000..b9fbd432
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/export/NoopSampledSpanStoreImplTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2018, 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.trace.export;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.common.Timestamp;
+import io.opencensus.implcore.internal.EventQueue;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.internal.TimestampConverter;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.testing.common.TestClock;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SampledSpanStore.ErrorFilter;
+import io.opencensus.trace.export.SampledSpanStore.LatencyFilter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link SampledSpanStoreImpl.NoopSampledSpanStoreImpl}. */
+@RunWith(JUnit4.class)
+public final class NoopSampledSpanStoreImplTest {
+
+ private static final String SPAN_NAME = "MySpanName";
+ private static final Collection<String> NAMES_FOR_COLLECTION =
+ Collections.<String>singletonList(SPAN_NAME);
+
+ private final Timestamp timestamp = Timestamp.create(1234, 5678);
+ private final Random random = new Random(1234);
+ private final SpanContext spanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random), SpanId.generateRandomId(random), TraceOptions.DEFAULT);
+ private final TestClock testClock = TestClock.create(timestamp);
+ private final TimestampConverter timestampConverter = TimestampConverter.now(testClock);
+ @Mock private StartEndHandler startEndHandler;
+ private RecordEventsSpanImpl recordEventsSpanImpl;
+ // maxSpansToReturn=0 means all
+ private final ErrorFilter errorFilter =
+ ErrorFilter.create(SPAN_NAME, null /* canonicalCode */, 0 /* maxSpansToReturn */);
+ private final LatencyFilter latencyFilter =
+ LatencyFilter.create(
+ SPAN_NAME,
+ 0 /* latencyLowerNs */,
+ Long.MAX_VALUE /* latencyUpperNs */,
+ 0 /* maxSpansToReturn */);
+ private final EventQueue eventQueue = new SimpleEventQueue();
+ private final SampledSpanStoreImpl sampledSpanStoreImpl =
+ ExportComponentImpl.createWithoutInProcessStores(eventQueue).getSampledSpanStore();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private void getMethodsShouldReturnEmpty() {
+ // get methods always return empty collections.
+ assertThat(sampledSpanStoreImpl.getSummary().getPerSpanNameSummary()).isEmpty();
+ assertThat(sampledSpanStoreImpl.getRegisteredSpanNamesForCollection()).isEmpty();
+ assertThat(sampledSpanStoreImpl.getErrorSampledSpans(errorFilter)).isEmpty();
+ assertThat(sampledSpanStoreImpl.getLatencySampledSpans(latencyFilter)).isEmpty();
+ }
+
+ @Test
+ public void noopImplementation() {
+ // None of the get methods should yield non-empty result.
+ getMethodsShouldReturnEmpty();
+
+ // registerSpanNamesForCollection() should do nothing and do not affect the result.
+ sampledSpanStoreImpl.registerSpanNamesForCollection(NAMES_FOR_COLLECTION);
+ getMethodsShouldReturnEmpty();
+
+ // considerForSampling() should do nothing and do not affect the result.
+ // It should be called after registerSpanNamesForCollection.
+ recordEventsSpanImpl =
+ RecordEventsSpanImpl.startSpan(
+ spanContext,
+ SPAN_NAME,
+ null,
+ null,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ timestampConverter,
+ testClock);
+ recordEventsSpanImpl.end();
+ sampledSpanStoreImpl.considerForSampling(recordEventsSpanImpl);
+ getMethodsShouldReturnEmpty();
+
+ // unregisterSpanNamesForCollection() should do nothing and do not affect the result.
+ sampledSpanStoreImpl.unregisterSpanNamesForCollection(NAMES_FOR_COLLECTION);
+ getMethodsShouldReturnEmpty();
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/export/SpanExporterImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/export/SpanExporterImplTest.java
new file mode 100644
index 00000000..f8f1d917
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/export/SpanExporterImplTest.java
@@ -0,0 +1,233 @@
+/*
+ * 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.trace.export;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Matchers.anyListOf;
+import static org.mockito.Mockito.doThrow;
+
+import io.opencensus.common.Duration;
+import io.opencensus.implcore.common.MillisClock;
+import io.opencensus.implcore.internal.SimpleEventQueue;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl;
+import io.opencensus.implcore.trace.RecordEventsSpanImpl.StartEndHandler;
+import io.opencensus.implcore.trace.StartEndHandlerImpl;
+import io.opencensus.testing.export.TestHandler;
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.config.TraceParams;
+import io.opencensus.trace.export.SpanData;
+import io.opencensus.trace.export.SpanExporter.Handler;
+import java.util.List;
+import java.util.Random;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link SpanExporterImpl}. */
+@RunWith(JUnit4.class)
+public class SpanExporterImplTest {
+ private static final String SPAN_NAME_1 = "MySpanName/1";
+ private static final String SPAN_NAME_2 = "MySpanName/2";
+ private final Random random = new Random(1234);
+ private final SpanContext sampledSpanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random),
+ SpanId.generateRandomId(random),
+ TraceOptions.builder().setIsSampled(true).build());
+ private final SpanContext notSampledSpanContext =
+ SpanContext.create(
+ TraceId.generateRandomId(random), SpanId.generateRandomId(random), TraceOptions.DEFAULT);
+ private final RunningSpanStoreImpl runningSpanStore = new InProcessRunningSpanStoreImpl();
+ private final TestHandler serviceHandler = new TestHandler();
+ @Mock private Handler mockServiceHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private RecordEventsSpanImpl createSampledEndedSpan(
+ StartEndHandler startEndHandler, String spanName) {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ sampledSpanContext,
+ spanName,
+ null,
+ null,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ null,
+ MillisClock.getInstance());
+ span.end();
+ return span;
+ }
+
+ private RecordEventsSpanImpl createNotSampledEndedSpan(
+ StartEndHandler startEndHandler, String spanName) {
+ RecordEventsSpanImpl span =
+ RecordEventsSpanImpl.startSpan(
+ notSampledSpanContext,
+ spanName,
+ null,
+ null,
+ false,
+ TraceParams.DEFAULT,
+ startEndHandler,
+ null,
+ MillisClock.getInstance());
+ span.end();
+ return span;
+ }
+
+ @Test
+ public void exportDifferentSampledSpans() {
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(1, 0));
+ StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(spanExporter, runningSpanStore, null, new SimpleEventQueue());
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ RecordEventsSpanImpl span1 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span2 = createSampledEndedSpan(startEndHandler, SPAN_NAME_2);
+ List<SpanData> exported = serviceHandler.waitForExport(2);
+ assertThat(exported).containsExactly(span1.toSpanData(), span2.toSpanData());
+ }
+
+ @Test
+ public void exportMoreSpansThanTheBufferSize() {
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(1, 0));
+ StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(spanExporter, runningSpanStore, null, new SimpleEventQueue());
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ RecordEventsSpanImpl span1 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span2 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span3 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span4 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span5 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span6 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ List<SpanData> exported = serviceHandler.waitForExport(6);
+ assertThat(exported)
+ .containsExactly(
+ span1.toSpanData(),
+ span2.toSpanData(),
+ span3.toSpanData(),
+ span4.toSpanData(),
+ span5.toSpanData(),
+ span6.toSpanData());
+ }
+
+ @Test
+ public void interruptWorkerThreadStops() throws InterruptedException {
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(1, 0));
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ Thread serviceExporterThread = spanExporter.getServiceExporterThread();
+ serviceExporterThread.interrupt();
+ // Test that the worker thread will stop.
+ serviceExporterThread.join();
+ }
+
+ @Test
+ public void serviceHandlerThrowsException() {
+ doThrow(new IllegalArgumentException("No export for you."))
+ .when(mockServiceHandler)
+ .export(anyListOf(SpanData.class));
+
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(1, 0));
+ StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(spanExporter, runningSpanStore, null, new SimpleEventQueue());
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ spanExporter.registerHandler("mock.service", mockServiceHandler);
+ RecordEventsSpanImpl span1 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ List<SpanData> exported = serviceHandler.waitForExport(1);
+ assertThat(exported).containsExactly(span1.toSpanData());
+ // Continue to export after the exception was received.
+ RecordEventsSpanImpl span2 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ exported = serviceHandler.waitForExport(1);
+ assertThat(exported).containsExactly(span2.toSpanData());
+ }
+
+ @Test
+ public void exportSpansToMultipleServices() {
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(1, 0));
+ StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(spanExporter, runningSpanStore, null, new SimpleEventQueue());
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ TestHandler serviceHandler2 = new TestHandler();
+ spanExporter.registerHandler("test.service2", serviceHandler2);
+ RecordEventsSpanImpl span1 = createSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span2 = createSampledEndedSpan(startEndHandler, SPAN_NAME_2);
+ List<SpanData> exported1 = serviceHandler.waitForExport(2);
+ List<SpanData> exported2 = serviceHandler2.waitForExport(2);
+ assertThat(exported1).containsExactly(span1.toSpanData(), span2.toSpanData());
+ assertThat(exported2).containsExactly(span1.toSpanData(), span2.toSpanData());
+ }
+
+ @Test
+ public void exportNotSampledSpans() {
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(1, 0));
+ StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(spanExporter, runningSpanStore, null, new SimpleEventQueue());
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ RecordEventsSpanImpl span1 = createNotSampledEndedSpan(startEndHandler, SPAN_NAME_1);
+ RecordEventsSpanImpl span2 = createSampledEndedSpan(startEndHandler, SPAN_NAME_2);
+ // Spans are recorded and exported in the same order as they are ended, we test that a non
+ // sampled span is not exported by creating and ending a sampled span after a non sampled span
+ // and checking that the first exported span is the sampled span (the non sampled did not get
+ // exported).
+ List<SpanData> exported = serviceHandler.waitForExport(1);
+ // Need to check this because otherwise the variable span1 is unused, other option is to not
+ // have a span1 variable.
+ assertThat(exported).doesNotContain(span1.toSpanData());
+ assertThat(exported).containsExactly(span2.toSpanData());
+ }
+
+ @Test(timeout = 10000L)
+ public void exportNotSampledSpansFlushed() {
+ // Set the export delay to zero, for no timeout, in order to confirm the #flush() below works
+ SpanExporterImpl spanExporter = SpanExporterImpl.create(4, Duration.create(0, 0));
+ StartEndHandler startEndHandler =
+ new StartEndHandlerImpl(spanExporter, runningSpanStore, null, new SimpleEventQueue());
+
+ spanExporter.registerHandler("test.service", serviceHandler);
+
+ RecordEventsSpanImpl span2 = createSampledEndedSpan(startEndHandler, SPAN_NAME_2);
+
+ // Force a flush, without this, the #waitForExport() call below would block indefinitely.
+ spanExporter.flush();
+
+ List<SpanData> exported = serviceHandler.waitForExport(1);
+
+ assertThat(exported).containsExactly(span2.toSpanData());
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveListTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveListTest.java
new file mode 100644
index 00000000..d7ac2ae8
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/internal/ConcurrentIntrusiveListTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.trace.internal;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.implcore.trace.internal.ConcurrentIntrusiveList.Element;
+import javax.annotation.Nullable;
+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 ConcurrentIntrusiveList}. */
+@RunWith(JUnit4.class)
+public class ConcurrentIntrusiveListTest {
+ private final ConcurrentIntrusiveList<FakeElement> intrusiveList =
+ new ConcurrentIntrusiveList<FakeElement>();
+ @Rule public final ExpectedException exception = ExpectedException.none();
+
+ @Test
+ public void emptyList() {
+ assertThat(intrusiveList.size()).isEqualTo(0);
+ assertThat(intrusiveList.getAll().isEmpty()).isTrue();
+ }
+
+ @Test
+ public void addRemoveAdd_SameElement() {
+ FakeElement element = new FakeElement();
+ intrusiveList.addElement(element);
+ assertThat(intrusiveList.size()).isEqualTo(1);
+ intrusiveList.removeElement(element);
+ assertThat(intrusiveList.size()).isEqualTo(0);
+ intrusiveList.addElement(element);
+ assertThat(intrusiveList.size()).isEqualTo(1);
+ }
+
+ @Test
+ public void addAndRemoveElements() {
+ FakeElement element1 = new FakeElement();
+ FakeElement element2 = new FakeElement();
+ FakeElement element3 = new FakeElement();
+ intrusiveList.addElement(element1);
+ intrusiveList.addElement(element2);
+ intrusiveList.addElement(element3);
+ assertThat(intrusiveList.size()).isEqualTo(3);
+ assertThat(intrusiveList.getAll()).containsExactly(element3, element2, element1).inOrder();
+ // Remove element from the middle of the list.
+ intrusiveList.removeElement(element2);
+ assertThat(intrusiveList.size()).isEqualTo(2);
+ assertThat(intrusiveList.getAll()).containsExactly(element3, element1).inOrder();
+ // Remove element from the tail of the list.
+ intrusiveList.removeElement(element1);
+ assertThat(intrusiveList.size()).isEqualTo(1);
+ assertThat(intrusiveList.getAll().contains(element3)).isTrue();
+ intrusiveList.addElement(element1);
+ assertThat(intrusiveList.size()).isEqualTo(2);
+ assertThat(intrusiveList.getAll()).containsExactly(element1, element3).inOrder();
+ // Remove element from the head of the list when there are other elements after.
+ intrusiveList.removeElement(element1);
+ assertThat(intrusiveList.size()).isEqualTo(1);
+ assertThat(intrusiveList.getAll().contains(element3)).isTrue();
+ // Remove element from the head of the list when no more other elements in the list.
+ intrusiveList.removeElement(element3);
+ assertThat(intrusiveList.size()).isEqualTo(0);
+ assertThat(intrusiveList.getAll().isEmpty()).isTrue();
+ }
+
+ @Test
+ public void addAlreadyAddedElement() {
+ FakeElement element = new FakeElement();
+ intrusiveList.addElement(element);
+ exception.expect(IllegalArgumentException.class);
+ intrusiveList.addElement(element);
+ }
+
+ @Test
+ public void removeNotAddedElement() {
+ FakeElement element = new FakeElement();
+ exception.expect(IllegalArgumentException.class);
+ intrusiveList.removeElement(element);
+ }
+
+ private static final class FakeElement implements Element<FakeElement> {
+ @Nullable private FakeElement next = null;
+ @Nullable private FakeElement prev = null;
+
+ @Override
+ public FakeElement getNext() {
+ return next;
+ }
+
+ @Override
+ public void setNext(FakeElement element) {
+ next = element;
+ }
+
+ @Override
+ public FakeElement getPrev() {
+ return prev;
+ }
+
+ @Override
+ public void setPrev(FakeElement element) {
+ prev = element;
+ }
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java
new file mode 100644
index 00000000..52e6bb3c
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/B3FormatTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.trace.propagation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_FLAGS;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_PARENT_SPAN_ID;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_SAMPLED;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_SPAN_ID;
+import static io.opencensus.implcore.trace.propagation.B3Format.X_B3_TRACE_ID;
+
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.propagation.SpanContextParseException;
+import io.opencensus.trace.propagation.TextFormat.Getter;
+import io.opencensus.trace.propagation.TextFormat.Setter;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+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 B3Format}. */
+@RunWith(JUnit4.class)
+public class B3FormatTest {
+ private static final String TRACE_ID_BASE16 = "ff000000000000000000000000000041";
+ private static final TraceId TRACE_ID = TraceId.fromLowerBase16(TRACE_ID_BASE16);
+ private static final String TRACE_ID_BASE16_EIGHT_BYTES = "0000000000000041";
+ private static final TraceId TRACE_ID_EIGHT_BYTES =
+ TraceId.fromLowerBase16("0000000000000000" + TRACE_ID_BASE16_EIGHT_BYTES);
+ private static final String SPAN_ID_BASE16 = "ff00000000000041";
+ private static final SpanId SPAN_ID = SpanId.fromLowerBase16(SPAN_ID_BASE16);
+ private static final byte TRACE_OPTIONS_BYTE = 1;
+ private static final TraceOptions TRACE_OPTIONS = TraceOptions.fromByte(TRACE_OPTIONS_BYTE);
+ private static final Setter<Map<String, String>> setter =
+ new Setter<Map<String, String>>() {
+ @Override
+ public void put(Map<String, String> carrier, String key, String value) {
+ carrier.put(key, value);
+ }
+ };
+ private static final Getter<Map<String, String>> getter =
+ new Getter<Map<String, String>>() {
+ @Nullable
+ @Override
+ public String get(Map<String, String> carrier, String key) {
+ return carrier.get(key);
+ }
+ };
+ private final B3Format b3Format = new B3Format();
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void serialize_SampledContext() {
+ Map<String, String> carrier = new HashMap<String, String>();
+ b3Format.inject(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS), carrier, setter);
+ assertThat(carrier)
+ .containsExactly(
+ X_B3_TRACE_ID, TRACE_ID_BASE16, X_B3_SPAN_ID, SPAN_ID_BASE16, X_B3_SAMPLED, "1");
+ }
+
+ @Test
+ public void serialize_NotSampledContext() {
+ Map<String, String> carrier = new HashMap<String, String>();
+ b3Format.inject(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT), carrier, setter);
+ assertThat(carrier)
+ .containsExactly(X_B3_TRACE_ID, TRACE_ID_BASE16, X_B3_SPAN_ID, SPAN_ID_BASE16);
+ }
+
+ @Test
+ public void parseMissingSampledAndMissingFlag() throws SpanContextParseException {
+ Map<String, String> headersNotSampled = new HashMap<String, String>();
+ headersNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ SpanContext spanContext = SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT);
+ assertThat(b3Format.extract(headersNotSampled, getter)).isEqualTo(spanContext);
+ }
+
+ @Test
+ public void parseSampled() throws SpanContextParseException {
+ Map<String, String> headersSampled = new HashMap<String, String>();
+ headersSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersSampled.put(X_B3_SAMPLED, "1");
+ assertThat(b3Format.extract(headersSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS));
+ }
+
+ @Test
+ public void parseZeroSampled() throws SpanContextParseException {
+ Map<String, String> headersNotSampled = new HashMap<String, String>();
+ headersNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersNotSampled.put(X_B3_SAMPLED, "0");
+ assertThat(b3Format.extract(headersNotSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test
+ public void parseFlag() throws SpanContextParseException {
+ Map<String, String> headersFlagSampled = new HashMap<String, String>();
+ headersFlagSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersFlagSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersFlagSampled.put(X_B3_FLAGS, "1");
+ assertThat(b3Format.extract(headersFlagSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS));
+ }
+
+ @Test
+ public void parseZeroFlag() throws SpanContextParseException {
+ Map<String, String> headersFlagNotSampled = new HashMap<String, String>();
+ headersFlagNotSampled.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ headersFlagNotSampled.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersFlagNotSampled.put(X_B3_FLAGS, "0");
+ assertThat(b3Format.extract(headersFlagNotSampled, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test
+ public void parseEightBytesTraceId() throws SpanContextParseException {
+ Map<String, String> headersEightBytes = new HashMap<String, String>();
+ headersEightBytes.put(X_B3_TRACE_ID, TRACE_ID_BASE16_EIGHT_BYTES);
+ headersEightBytes.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ headersEightBytes.put(X_B3_SAMPLED, "1");
+ assertThat(b3Format.extract(headersEightBytes, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID_EIGHT_BYTES, SPAN_ID, TRACE_OPTIONS));
+ }
+
+ @Test
+ public void parseEightBytesTraceId_NotSampledSpanContext() throws SpanContextParseException {
+ Map<String, String> headersEightBytes = new HashMap<String, String>();
+ headersEightBytes.put(X_B3_TRACE_ID, TRACE_ID_BASE16_EIGHT_BYTES);
+ headersEightBytes.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ assertThat(b3Format.extract(headersEightBytes, getter))
+ .isEqualTo(SpanContext.create(TRACE_ID_EIGHT_BYTES, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test
+ public void parseInvalidTraceId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, "abcdefghijklmnop");
+ invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseInvalidTraceId_Size() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, "0123456789abcdef00");
+ invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseMissingTraceId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_SPAN_ID, SPAN_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Missing X_B3_TRACE_ID.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseInvalidSpanId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ invalidHeaders.put(X_B3_SPAN_ID, "abcdefghijklmnop");
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseInvalidSpanId_Size() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ invalidHeaders.put(X_B3_SPAN_ID, "0123456789abcdef00");
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Invalid input.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void parseMissingSpanId() throws SpanContextParseException {
+ Map<String, String> invalidHeaders = new HashMap<String, String>();
+ invalidHeaders.put(X_B3_TRACE_ID, TRACE_ID_BASE16);
+ thrown.expect(SpanContextParseException.class);
+ thrown.expectMessage("Missing X_B3_SPAN_ID.");
+ b3Format.extract(invalidHeaders, getter);
+ }
+
+ @Test
+ public void fields_list() {
+ assertThat(b3Format.fields())
+ .containsExactly(
+ X_B3_TRACE_ID, X_B3_SPAN_ID, X_B3_PARENT_SPAN_ID, X_B3_SAMPLED, X_B3_FLAGS);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/BinaryFormatImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/BinaryFormatImplTest.java
new file mode 100644
index 00000000..f43be479
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/BinaryFormatImplTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.trace.propagation;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import io.opencensus.trace.SpanContext;
+import io.opencensus.trace.SpanId;
+import io.opencensus.trace.TraceId;
+import io.opencensus.trace.TraceOptions;
+import io.opencensus.trace.propagation.BinaryFormat;
+import io.opencensus.trace.propagation.SpanContextParseException;
+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 BinaryFormatImpl}. */
+@RunWith(JUnit4.class)
+public class BinaryFormatImplTest {
+ private static final byte[] TRACE_ID_BYTES =
+ new byte[] {64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79};
+ private static final TraceId TRACE_ID = TraceId.fromBytes(TRACE_ID_BYTES);
+ private static final byte[] SPAN_ID_BYTES = new byte[] {97, 98, 99, 100, 101, 102, 103, 104};
+ private static final SpanId SPAN_ID = SpanId.fromBytes(SPAN_ID_BYTES);
+ private static final byte TRACE_OPTIONS_BYTES = 1;
+ private static final TraceOptions TRACE_OPTIONS = TraceOptions.fromByte(TRACE_OPTIONS_BYTES);
+ private static final byte[] EXAMPLE_BYTES =
+ new byte[] {
+ 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, 98, 99, 100,
+ 101, 102, 103, 104, 2, 1
+ };
+ private static final SpanContext EXAMPLE_SPAN_CONTEXT =
+ SpanContext.create(TRACE_ID, SPAN_ID, TRACE_OPTIONS);
+ @Rule public ExpectedException expectedException = ExpectedException.none();
+ private final BinaryFormat binaryFormat = new BinaryFormatImpl();
+
+ private void testSpanContextConversion(SpanContext spanContext) throws SpanContextParseException {
+ SpanContext propagatedBinarySpanContext =
+ binaryFormat.fromByteArray(binaryFormat.toByteArray(spanContext));
+
+ assertWithMessage("BinaryFormat propagated context is not equal with the initial context.")
+ .that(propagatedBinarySpanContext)
+ .isEqualTo(spanContext);
+ }
+
+ @Test
+ public void propagate_SpanContextTracingEnabled() throws SpanContextParseException {
+ testSpanContextConversion(
+ SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.builder().setIsSampled(true).build()));
+ }
+
+ @Test
+ public void propagate_SpanContextNoTracing() throws SpanContextParseException {
+ testSpanContextConversion(SpanContext.create(TRACE_ID, SPAN_ID, TraceOptions.DEFAULT));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void toBinaryValue_NullSpanContext() {
+ binaryFormat.toByteArray(null);
+ }
+
+ @Test
+ public void toBinaryValue_InvalidSpanContext() {
+ assertThat(binaryFormat.toByteArray(SpanContext.INVALID))
+ .isEqualTo(
+ new byte[] {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0
+ });
+ }
+
+ @Test
+ public void fromBinaryValue_BinaryExampleValue() throws SpanContextParseException {
+ assertThat(binaryFormat.fromByteArray(EXAMPLE_BYTES)).isEqualTo(EXAMPLE_SPAN_CONTEXT);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void fromBinaryValue_NullInput() throws SpanContextParseException {
+ binaryFormat.fromByteArray(null);
+ }
+
+ @Test
+ public void fromBinaryValue_EmptyInput() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage("Unsupported version.");
+ binaryFormat.fromByteArray(new byte[0]);
+ }
+
+ @Test
+ public void fromBinaryValue_UnsupportedVersionId() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage("Unsupported version.");
+ binaryFormat.fromByteArray(
+ new byte[] {
+ 66, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 97, 98, 99, 100, 101,
+ 102, 103, 104, 1
+ });
+ }
+
+ @Test
+ public void fromBinaryValue_UnsupportedFieldIdFirst() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage(
+ "Invalid input: expected trace ID at offset " + BinaryFormatImpl.TRACE_ID_FIELD_ID_OFFSET);
+ binaryFormat.fromByteArray(
+ new byte[] {
+ 0, 4, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, 98, 99, 100,
+ 101, 102, 103, 104, 2, 1
+ });
+ }
+
+ @Test
+ public void fromBinaryValue_UnsupportedFieldIdSecond() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage(
+ "Invalid input: expected span ID at offset " + BinaryFormatImpl.SPAN_ID_FIELD_ID_OFFSET);
+ binaryFormat.fromByteArray(
+ new byte[] {
+ 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 3, 97, 98, 99, 100,
+ 101, 102, 103, 104, 2, 1
+ });
+ }
+
+ @Test
+ public void fromBinaryValue_UnsupportedFieldIdThird_skipped() throws SpanContextParseException {
+ assertThat(
+ binaryFormat
+ .fromByteArray(
+ new byte[] {
+ 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97,
+ 98, 99, 100, 101, 102, 103, 104, 0, 1
+ })
+ .isValid())
+ .isTrue();
+ }
+
+ @Test
+ public void fromBinaryValue_ShorterTraceId() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage("Invalid input: truncated");
+ binaryFormat.fromByteArray(
+ new byte[] {0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76});
+ }
+
+ @Test
+ public void fromBinaryValue_ShorterSpanId() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage("Invalid input: truncated");
+ binaryFormat.fromByteArray(new byte[] {0, 1, 97, 98, 99, 100, 101, 102, 103});
+ }
+
+ @Test
+ public void fromBinaryValue_ShorterTraceOptions() throws SpanContextParseException {
+ expectedException.expect(SpanContextParseException.class);
+ expectedException.expectMessage("Invalid input: truncated");
+ binaryFormat.fromByteArray(
+ new byte[] {
+ 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, 98, 99, 100,
+ 101, 102, 103, 104, 2
+ });
+ }
+
+ @Test
+ public void fromBinaryValue_MissingTraceOptionsOk() throws SpanContextParseException {
+ SpanContext extracted =
+ binaryFormat.fromByteArray(
+ new byte[] {
+ 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 1, 97, 98, 99,
+ 100, 101, 102, 103, 104
+ });
+
+ assertThat(extracted.isValid()).isTrue();
+ assertThat(extracted.getTraceOptions()).isEqualTo(TraceOptions.DEFAULT);
+ }
+}
diff --git a/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/PropagationComponentImplTest.java b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/PropagationComponentImplTest.java
new file mode 100644
index 00000000..00ed90fe
--- /dev/null
+++ b/impl_core/src/test/java/io/opencensus/implcore/trace/propagation/PropagationComponentImplTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.trace.propagation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import io.opencensus.trace.propagation.PropagationComponent;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link PropagationComponentImpl}. */
+@RunWith(JUnit4.class)
+public class PropagationComponentImplTest {
+ private final PropagationComponent propagationComponent = new PropagationComponentImpl();
+
+ @Test
+ public void implementationOfBinary() {
+ assertThat(propagationComponent.getBinaryFormat()).isInstanceOf(BinaryFormatImpl.class);
+ }
+
+ @Test
+ public void implementationOfB3Format() {
+ assertThat(propagationComponent.getB3Format()).isInstanceOf(B3Format.class);
+ }
+}