diff options
Diffstat (limited to 'api/src')
30 files changed, 2831 insertions, 2 deletions
diff --git a/api/src/main/java/io/opencensus/common/ToDoubleFunction.java b/api/src/main/java/io/opencensus/common/ToDoubleFunction.java index 415f8ca9..eac85793 100644 --- a/api/src/main/java/io/opencensus/common/ToDoubleFunction.java +++ b/api/src/main/java/io/opencensus/common/ToDoubleFunction.java @@ -17,7 +17,7 @@ package io.opencensus.common; /** - * Represents a function that produces a double-valued result. See {@code + * Represents a function that produces a double-valued result. See {@link * io.opencensus.metrics.MetricRegistry} for an example of its use. * * <p>Note: This class is based on the java.util.ToDoubleFunction class added in Java 1.8. We cannot diff --git a/api/src/main/java/io/opencensus/common/ToLongFunction.java b/api/src/main/java/io/opencensus/common/ToLongFunction.java index 727a8b44..26a27b18 100644 --- a/api/src/main/java/io/opencensus/common/ToLongFunction.java +++ b/api/src/main/java/io/opencensus/common/ToLongFunction.java @@ -17,7 +17,7 @@ package io.opencensus.common; /** - * Represents a function that produces a long-valued result. See {@code + * Represents a function that produces a long-valued result. See {@link * io.opencensus.metrics.MetricRegistry} for an example of its use. * * <p>Note: This class is based on the java.util.ToLongFunction class added in Java 1.8. We cannot diff --git a/api/src/main/java/io/opencensus/metrics/Distribution.java b/api/src/main/java/io/opencensus/metrics/Distribution.java new file mode 100644 index 00000000..bdea7762 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/Distribution.java @@ -0,0 +1,280 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.common.Timestamp; +import io.opencensus.internal.Utils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * {@link Distribution} contains summary statistics for a population of values. It optionally + * contains a histogram representing the distribution of those values across a set of buckets. + * + * @since 0.16 + */ +@ExperimentalApi +@AutoValue +@Immutable +public abstract class Distribution { + + Distribution() {} + + /** + * Creates a {@link Distribution}. + * + * @param mean mean of the population values. + * @param count count of the population values. + * @param sumOfSquaredDeviations sum of squared deviations of the population values. + * @param bucketBoundaries bucket boundaries of a histogram. + * @param buckets {@link Bucket}s of a histogram. + * @return a {@code Distribution}. + * @since 0.16 + */ + public static Distribution create( + double mean, + long count, + double sumOfSquaredDeviations, + List<Double> bucketBoundaries, + List<Bucket> buckets) { + Utils.checkArgument(count >= 0, "count should be non-negative."); + Utils.checkArgument( + sumOfSquaredDeviations >= 0, "sum of squared deviations should be non-negative."); + if (count == 0) { + Utils.checkArgument(mean == 0, "mean should be 0 if count is 0."); + Utils.checkArgument( + sumOfSquaredDeviations == 0, "sum of squared deviations should be 0 if count is 0."); + } + return new AutoValue_Distribution( + mean, + count, + sumOfSquaredDeviations, + copyBucketBounds(bucketBoundaries), + copyBucketCount(buckets)); + } + + private static List<Double> copyBucketBounds(List<Double> bucketBoundaries) { + Utils.checkNotNull(bucketBoundaries, "bucketBoundaries list should not be null."); + List<Double> bucketBoundariesCopy = new ArrayList<Double>(bucketBoundaries); // Deep copy. + // Check if sorted. + if (bucketBoundariesCopy.size() > 1) { + double lower = bucketBoundariesCopy.get(0); + for (int i = 1; i < bucketBoundariesCopy.size(); i++) { + double next = bucketBoundariesCopy.get(i); + Utils.checkArgument(lower < next, "bucket boundaries not sorted."); + lower = next; + } + } + return Collections.unmodifiableList(bucketBoundariesCopy); + } + + private static List<Bucket> copyBucketCount(List<Bucket> buckets) { + Utils.checkNotNull(buckets, "bucket list should not be null."); + List<Bucket> bucketsCopy = new ArrayList<Bucket>(buckets); + for (Bucket bucket : bucketsCopy) { + Utils.checkNotNull(bucket, "bucket should not be null."); + } + return Collections.unmodifiableList(bucketsCopy); + } + + /** + * Returns the aggregated mean. + * + * @return the aggregated mean. + * @since 0.16 + */ + public abstract double getMean(); + + /** + * Returns the aggregated count. + * + * @return the aggregated count. + * @since 0.16 + */ + public abstract long getCount(); + + /** + * Returns the aggregated sum of squared deviations. + * + * <p>The sum of squared deviations from the mean of the values in the population. For values x_i + * this is: + * + * <p>Sum[i=1..n]((x_i - mean)^2) + * + * <p>If count is zero then this field must be zero. + * + * @return the aggregated sum of squared deviations. + * @since 0.16 + */ + public abstract double getSumOfSquaredDeviations(); + + /** + * Returns the bucket boundaries of this distribution. + * + * <p>The bucket boundaries for that histogram are described by bucket_bounds. This defines + * size(bucket_bounds) + 1 (= N) buckets. The boundaries for bucket index i are: + * + * <ul> + * <li>{@code (-infinity, bucket_bounds[i]) for i == 0} + * <li>{@code [bucket_bounds[i-1], bucket_bounds[i]) for 0 < i < N-2} + * <li>{@code [bucket_bounds[i-1], +infinity) for i == N-1} + * </ul> + * + * <p>i.e. an underflow bucket (number 0), zero or more finite buckets (1 through N - 2, and an + * overflow bucket (N - 1), with inclusive lower bounds and exclusive upper bounds. + * + * <p>If bucket_bounds has no elements (zero size), then there is no histogram associated with the + * Distribution. If bucket_bounds has only one element, there are no finite buckets, and that + * single element is the common boundary of the overflow and underflow buckets. The values must be + * monotonically increasing. + * + * @return the bucket boundaries of this distribution. + * @since 0.16 + */ + public abstract List<Double> getBucketBoundaries(); + + /** + * Returns the aggregated histogram {@link Bucket}s. + * + * @return the aggregated histogram buckets. + * @since 0.16 + */ + public abstract List<Bucket> getBuckets(); + + /** + * The histogram bucket of the population values. + * + * @since 0.16 + */ + @AutoValue + @Immutable + public abstract static class Bucket { + + Bucket() {} + + /** + * Creates a {@link Bucket}. + * + * @param count the number of values in each bucket of the histogram. + * @return a {@code Bucket}. + * @since 0.16 + */ + public static Bucket create(long count) { + Utils.checkArgument(count >= 0, "bucket count should be non-negative."); + return new AutoValue_Distribution_Bucket(count, null); + } + + /** + * Creates a {@link Bucket} with an {@link Exemplar}. + * + * @param count the number of values in each bucket of the histogram. + * @param exemplar the {@code Exemplar} of this {@code Bucket}. + * @return a {@code Bucket}. + * @since 0.16 + */ + public static Bucket create(long count, Exemplar exemplar) { + Utils.checkArgument(count >= 0, "bucket count should be non-negative."); + Utils.checkNotNull(exemplar, "exemplar"); + return new AutoValue_Distribution_Bucket(count, exemplar); + } + + /** + * Returns the number of values in each bucket of the histogram. + * + * @return the number of values in each bucket of the histogram. + * @since 0.16 + */ + public abstract long getCount(); + + /** + * Returns the {@link Exemplar} associated with the {@link Bucket}, or {@code null} if there + * isn't one. + * + * @return the {@code Exemplar} associated with the {@code Bucket}, or {@code null} if there + * isn't one. + * @since 0.16 + */ + @Nullable + public abstract Exemplar getExemplar(); + } + + /** + * An example point that may be used to annotate aggregated distribution values, associated with a + * histogram bucket. + * + * @since 0.16 + */ + @Immutable + @AutoValue + public abstract static class Exemplar { + + Exemplar() {} + + /** + * Returns value of the {@link Exemplar} point. + * + * @return value of the {@code Exemplar} point. + * @since 0.16 + */ + public abstract double getValue(); + + /** + * Returns the time that this {@link Exemplar}'s value was recorded. + * + * @return the time that this {@code Exemplar}'s value was recorded. + * @since 0.16 + */ + public abstract Timestamp getTimestamp(); + + /** + * Returns the contextual information about the example value, represented as a string map. + * + * @return the contextual information about the example value. + * @since 0.16 + */ + public abstract Map<String, String> getAttachments(); + + /** + * Creates an {@link Exemplar}. + * + * @param value value of the {@link Exemplar} point. + * @param timestamp the time that this {@code Exemplar}'s value was recorded. + * @param attachments the contextual information about the example value. + * @return an {@code Exemplar}. + * @since 0.16 + */ + public static Exemplar create( + double value, Timestamp timestamp, Map<String, String> attachments) { + Utils.checkNotNull(attachments, "attachments"); + Map<String, String> attachmentsCopy = + Collections.unmodifiableMap(new HashMap<String, String>(attachments)); + for (Entry<String, String> entry : attachmentsCopy.entrySet()) { + Utils.checkNotNull(entry.getKey(), "key of attachments"); + Utils.checkNotNull(entry.getValue(), "value of attachments"); + } + return new AutoValue_Distribution_Exemplar(value, timestamp, attachmentsCopy); + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/LabelKey.java b/api/src/main/java/io/opencensus/metrics/LabelKey.java new file mode 100644 index 00000000..01ef0b55 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/LabelKey.java @@ -0,0 +1,62 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import javax.annotation.concurrent.Immutable; + +/** + * The key of a {@code Label} associated with a {@code MetricDescriptor}. + * + * @since 0.15 + */ +@ExperimentalApi +@Immutable +@AutoValue +public abstract class LabelKey { + + LabelKey() {} + + /** + * Creates a {@link LabelKey}. + * + * @param key the key of a {@code Label}. + * @param description a human-readable description of what this label key represents. + * @return a {@code LabelKey}. + * @since 0.15 + */ + public static LabelKey create(String key, String description) { + return new AutoValue_LabelKey(key, description); + } + + /** + * Returns the key of this {@link LabelKey}. + * + * @return the key. + * @since 0.15 + */ + public abstract String getKey(); + + /** + * Returns the description of this {@link LabelKey}. + * + * @return the description. + * @since 0.15 + */ + public abstract String getDescription(); +} diff --git a/api/src/main/java/io/opencensus/metrics/LabelValue.java b/api/src/main/java/io/opencensus/metrics/LabelValue.java new file mode 100644 index 00000000..e81b63d2 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/LabelValue.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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * The value of a {@code Label} associated with a {@code TimeSeries}. + * + * @since 0.15 + */ +@ExperimentalApi +@Immutable +@AutoValue +public abstract class LabelValue { + + LabelValue() {} + + /** + * Creates a {@link LabelValue}. + * + * @param value the value of a {@code Label}. {@code null} value indicates an unset {@code + * LabelValue}. + * @return a {@code LabelValue}. + * @since 0.15 + */ + public static LabelValue create(@Nullable String value) { + return new AutoValue_LabelValue(value); + } + + /** + * Returns the value of this {@link LabelValue}. Returns {@code null} if the value is unset and + * supposed to be ignored. + * + * @return the value. + * @since 0.15 + */ + @Nullable + public abstract String getValue(); +} diff --git a/api/src/main/java/io/opencensus/metrics/Metric.java b/api/src/main/java/io/opencensus/metrics/Metric.java new file mode 100644 index 00000000..fa0018b3 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/Metric.java @@ -0,0 +1,98 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.internal.Utils; +import io.opencensus.metrics.Value.ValueDistribution; +import io.opencensus.metrics.Value.ValueDouble; +import io.opencensus.metrics.Value.ValueLong; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * A {@link Metric} with one or more {@link TimeSeries}. + * + * @since 0.16 + */ +@ExperimentalApi +@Immutable +@AutoValue +public abstract class Metric { + + Metric() {} + + /** + * Creates a {@link Metric}. + * + * @param metricDescriptor the {@link MetricDescriptor}. + * @param timeSeriesList the {@link TimeSeries} list for this metric. + * @return a {@code Metric}. + * @since 0.16 + */ + public static Metric create(MetricDescriptor metricDescriptor, List<TimeSeries> timeSeriesList) { + checkTypeMatch(metricDescriptor.getType(), timeSeriesList); + return new AutoValue_Metric(metricDescriptor, timeSeriesList); + } + + /** + * Returns the {@link MetricDescriptor} of this metric. + * + * @return the {@code MetricDescriptor} of this metric. + * @since 0.16 + */ + public abstract MetricDescriptor getMetricDescriptor(); + + /** + * Returns the {@link TimeSeries} list for this metric. + * + * <p>The type of the {@link TimeSeries#getPoints()} must match {@link MetricDescriptor.Type}. + * + * @return the {@code TimeSeriesList} for this metric. + * @since 0.16 + */ + public abstract List<TimeSeries> getTimeSeriesList(); + + private static void checkTypeMatch(MetricDescriptor.Type type, List<TimeSeries> timeSeriesList) { + for (TimeSeries timeSeries : timeSeriesList) { + for (Point point : timeSeries.getPoints()) { + Value value = point.getValue(); + String valueClassName = ""; + if (value.getClass().getSuperclass() != null) { // work around nullness check + // AutoValue classes should always have a super class. + valueClassName = value.getClass().getSuperclass().getSimpleName(); + } + switch (type) { + case GAUGE_INT64: + case CUMULATIVE_INT64: + Utils.checkArgument( + value instanceof ValueLong, "Type mismatch: %s, %s.", type, valueClassName); + break; + case CUMULATIVE_DOUBLE: + case GAUGE_DOUBLE: + Utils.checkArgument( + value instanceof ValueDouble, "Type mismatch: %s, %s.", type, valueClassName); + break; + case CUMULATIVE_DISTRIBUTION: + Utils.checkArgument( + value instanceof ValueDistribution, "Type mismatch: %s, %s.", type, valueClassName); + } + } + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java b/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java new file mode 100644 index 00000000..4558446a --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/MetricDescriptor.java @@ -0,0 +1,150 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.internal.Utils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.concurrent.Immutable; + +/** + * {@link MetricDescriptor} defines a {@code Metric} type and its schema. + * + * @since 0.16 + */ +@ExperimentalApi +@Immutable +@AutoValue +public abstract class MetricDescriptor { + + MetricDescriptor() {} + + /** + * Creates a {@link MetricDescriptor}. + * + * @param name name of {@code MetricDescriptor}. + * @param description description of {@code MetricDescriptor}. + * @param unit the metric unit. + * @param type type of {@code MetricDescriptor}. + * @param labelKeys the label keys associated with the {@code MetricDescriptor}. + * @return a {@code MetricDescriptor}. + * @since 0.16 + */ + public static MetricDescriptor create( + String name, String description, String unit, Type type, List<LabelKey> labelKeys) { + Utils.checkNotNull(labelKeys, "labelKeys"); + Utils.checkListElementNotNull(labelKeys, "labelKey"); + return new AutoValue_MetricDescriptor( + name, + description, + unit, + type, + Collections.unmodifiableList(new ArrayList<LabelKey>(labelKeys))); + } + + /** + * Returns the metric descriptor name. + * + * @return the metric descriptor name. + * @since 0.16 + */ + public abstract String getName(); + + /** + * Returns the description of this metric descriptor. + * + * @return the description of this metric descriptor. + * @since 0.16 + */ + public abstract String getDescription(); + + /** + * Returns the unit of this metric descriptor. + * + * @return the unit of this metric descriptor. + * @since 0.16 + */ + public abstract String getUnit(); + + /** + * Returns the type of this metric descriptor. + * + * @return the type of this metric descriptor. + * @since 0.16 + */ + public abstract Type getType(); + + /** + * Returns the label keys associated with this metric descriptor. + * + * @return the label keys associated with this metric descriptor. + * @since 0.16 + */ + public abstract List<LabelKey> getLabelKeys(); + + /** + * The kind of metric. It describes how the data is reported. + * + * <p>A gauge is an instantaneous measurement of a value. + * + * <p>A cumulative measurement is a value accumulated over a time interval. In a time series, + * cumulative measurements should have the same start time and increasing end times, until an + * event resets the cumulative value to zero and sets a new start time for the following points. + * + * @since 0.16 + */ + public enum Type { + + /** + * An instantaneous measurement of an int64 value. + * + * @since 0.16 + */ + GAUGE_INT64, + + /** + * An instantaneous measurement of a double value. + * + * @since 0.16 + */ + GAUGE_DOUBLE, + + /** + * An cumulative measurement of an int64 value. + * + * @since 0.16 + */ + CUMULATIVE_INT64, + + /** + * An cumulative measurement of a double value. + * + * @since 0.16 + */ + CUMULATIVE_DOUBLE, + + /** + * An cumulative measurement of a distribution value. + * + * @since 0.16 + */ + CUMULATIVE_DISTRIBUTION, + } +} diff --git a/api/src/main/java/io/opencensus/metrics/MetricProducer.java b/api/src/main/java/io/opencensus/metrics/MetricProducer.java new file mode 100644 index 00000000..38b5d571 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/MetricProducer.java @@ -0,0 +1,38 @@ +/* + * 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.metrics; + +import io.opencensus.common.ExperimentalApi; +import io.opencensus.metrics.export.MetricProducerManager; +import java.util.Collection; + +/** + * A {@link io.opencensus.metrics.Metric} producer that can be registered for exporting using {@link + * MetricProducerManager}. + * + * <p>All implementation MUST be thread-safe. + */ +@ExperimentalApi +public abstract class MetricProducer { + + /** + * Returns a collection of produced {@link io.opencensus.metrics.Metric}s to be exported. + * + * @return a collection of produced {@link io.opencensus.metrics.Metric}s to be exported. + */ + public abstract Collection<Metric> getMetrics(); +} diff --git a/api/src/main/java/io/opencensus/metrics/MetricRegistry.java b/api/src/main/java/io/opencensus/metrics/MetricRegistry.java new file mode 100644 index 00000000..ae4d90b0 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/MetricRegistry.java @@ -0,0 +1,117 @@ +/* + * 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.metrics; + +import io.opencensus.common.ExperimentalApi; +import io.opencensus.common.ToDoubleFunction; +import io.opencensus.common.ToLongFunction; +import io.opencensus.internal.Utils; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; + +/** + * Creates and manages your application's set of metrics. Exporters use the metric registry to + * iterate over the set of metrics instrumenting your application, and then further export each + * metric to the backend of choice. + * + * @since 0.16 + */ +@ExperimentalApi +public abstract class MetricRegistry extends MetricProducer { + /** + * Build a new long gauge to be added to the registry. + * + * <p>Must be called only once. + * + * @param name the name of the metric. + * @param description the description of the metric. + * @param unit the unit of the metric. + * @param obj the function argument. + * @param function the function to be called. + * @since 0.16 @ExperimentalApi + */ + public abstract <T> void addLongGauge( + String name, + String description, + String unit, + LinkedHashMap<LabelKey, LabelValue> labels, + T obj, + ToLongFunction<T> function); + + /** + * Build a new double gauge to be added to the registry. + * + * <p>Must be called only once. + * + * @param name the name of the metric. + * @param description the description of the metric. + * @param unit the unit of the metric. + * @param obj the function argument. + * @param function the function to be called. + * @since 0.16 @ExperimentalApi + */ + public abstract <T> void addDoubleGauge( + String name, + String description, + String unit, + LinkedHashMap<LabelKey, LabelValue> labels, + T obj, + ToDoubleFunction<T> function); + + static MetricRegistry newNoopMetricRegistry() { + return new NoopMetricRegistry(); + } + + private static final class NoopMetricRegistry extends MetricRegistry { + + @Override + public <T> void addLongGauge( + String name, + String description, + String unit, + LinkedHashMap<LabelKey, LabelValue> labels, + T obj, + ToLongFunction<T> function) { + Utils.checkNotNull(name, "name"); + Utils.checkNotNull(description, "description"); + Utils.checkNotNull(unit, "unit"); + Utils.checkNotNull(labels, "labels"); + Utils.checkNotNull(function, "function"); + } + + @Override + public <T> void addDoubleGauge( + String name, + String description, + String unit, + LinkedHashMap<LabelKey, LabelValue> labels, + T obj, + ToDoubleFunction<T> function) { + Utils.checkNotNull(name, "name"); + Utils.checkNotNull(description, "description"); + Utils.checkNotNull(unit, "unit"); + Utils.checkNotNull(labels, "labels"); + Utils.checkNotNull(function, "function"); + } + + @Override + public Collection<Metric> getMetrics() { + return Collections.emptyList(); + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/Metrics.java b/api/src/main/java/io/opencensus/metrics/Metrics.java new file mode 100644 index 00000000..521fbd08 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/Metrics.java @@ -0,0 +1,96 @@ +/* + * 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.metrics; + +import io.opencensus.common.ExperimentalApi; +import io.opencensus.internal.DefaultVisibilityForTesting; +import io.opencensus.internal.Provider; +import io.opencensus.metrics.export.ExportComponent; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +/** + * Class for accessing the default {@link MetricsComponent}. + * + * @since 0.16 + */ +@ExperimentalApi +public final class Metrics { + private static final Logger logger = Logger.getLogger(Metrics.class.getName()); + private static final MetricsComponent metricsComponent = + loadMetricsComponent(MetricsComponent.class.getClassLoader()); + + /** + * Returns the global {@link ExportComponent}. + * + * @return the global {@code ExportComponent}. + * @since 0.16 + */ + public static ExportComponent getExportComponent() { + return metricsComponent.getExportComponent(); + } + + /** + * Returns the global {@link MetricRegistry}. + * + * <p>This {@code MetricRegistry} is already added to the global {@link + * io.opencensus.metrics.export.MetricProducerManager}. + * + * @return the global {@code MetricRegistry}. + * @since 0.16 + */ + public static MetricRegistry getMetricRegistry() { + return metricsComponent.getMetricRegistry(); + } + + // Any provider that may be used for MetricsComponent can be added here. + @DefaultVisibilityForTesting + static MetricsComponent loadMetricsComponent(@Nullable ClassLoader classLoader) { + try { + // Call Class.forName with literal string name of the class to help shading tools. + return Provider.createInstance( + Class.forName( + "io.opencensus.impl.metrics.MetricsComponentImpl", /*initialize=*/ true, classLoader), + MetricsComponent.class); + } catch (ClassNotFoundException e) { + logger.log( + Level.FINE, + "Couldn't load full implementation for MetricsComponent, now trying to load lite " + + "implementation.", + e); + } + try { + // Call Class.forName with literal string name of the class to help shading tools. + return Provider.createInstance( + Class.forName( + "io.opencensus.impllite.metrics.MetricsComponentImplLite", + /*initialize=*/ true, + classLoader), + MetricsComponent.class); + } catch (ClassNotFoundException e) { + logger.log( + Level.FINE, + "Couldn't load lite implementation for MetricsComponent, now using default " + + "implementation for MetricsComponent.", + e); + } + return MetricsComponent.newNoopMetricsComponent(); + } + + private Metrics() {} +} diff --git a/api/src/main/java/io/opencensus/metrics/MetricsComponent.java b/api/src/main/java/io/opencensus/metrics/MetricsComponent.java new file mode 100644 index 00000000..08d954ef --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/MetricsComponent.java @@ -0,0 +1,71 @@ +/* + * 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.metrics; + +import io.opencensus.common.ExperimentalApi; +import io.opencensus.metrics.export.ExportComponent; + +/** + * Class that holds the implementation instance for {@link ExportComponent}. + * + * @since 0.16 + */ +@ExperimentalApi +public abstract class MetricsComponent { + + /** + * Returns the {@link ExportComponent} with the provided implementation. If no implementation is + * provided then no-op implementations will be used. + * + * @return the {@link ExportComponent} implementation. + * @since 0.16 + */ + public abstract ExportComponent getExportComponent(); + + /** + * Returns the {@link MetricRegistry} with the provided implementation. + * + * @return the {@link MetricRegistry} implementation. + * @since 0.16 + */ + public abstract MetricRegistry getMetricRegistry(); + + /** + * Returns an instance that contains no-op implementations for all the instances. + * + * @return an instance that contains no-op implementations for all the instances. + */ + static MetricsComponent newNoopMetricsComponent() { + return new NoopMetricsComponent(); + } + + private static final class NoopMetricsComponent extends MetricsComponent { + private static final ExportComponent EXPORT_COMPONENT = + ExportComponent.newNoopExportComponent(); + private static final MetricRegistry METRIC_REGISTRY = MetricRegistry.newNoopMetricRegistry(); + + @Override + public ExportComponent getExportComponent() { + return EXPORT_COMPONENT; + } + + @Override + public MetricRegistry getMetricRegistry() { + return METRIC_REGISTRY; + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/Point.java b/api/src/main/java/io/opencensus/metrics/Point.java new file mode 100644 index 00000000..e66b273d --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/Point.java @@ -0,0 +1,63 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.common.Timestamp; +import javax.annotation.concurrent.Immutable; + +/** + * A timestamped measurement of a {@code TimeSeries}. + * + * @since 0.16 + */ +@ExperimentalApi +@AutoValue +@Immutable +public abstract class Point { + + Point() {} + + /** + * Creates a {@link Point}. + * + * @param value the {@link Value} of this {@link Point}. + * @param timestamp the {@link Timestamp} when this {@link Point} was recorded. + * @return a {@code Point}. + * @since 0.16 + */ + public static Point create(Value value, Timestamp timestamp) { + return new AutoValue_Point(value, timestamp); + } + + /** + * Returns the {@link Value}. + * + * @return the {@code Value}. + * @since 0.16 + */ + public abstract Value getValue(); + + /** + * Returns the {@link Timestamp} when this {@link Point} was recorded. + * + * @return the {@code Timestamp}. + * @since 0.16 + */ + public abstract Timestamp getTimestamp(); +} diff --git a/api/src/main/java/io/opencensus/metrics/TimeSeries.java b/api/src/main/java/io/opencensus/metrics/TimeSeries.java new file mode 100644 index 00000000..c62b6b72 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/TimeSeries.java @@ -0,0 +1,94 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.common.Timestamp; +import io.opencensus.internal.Utils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * A collection of data points that describes the time-varying values of a {@code Metric}. + * + * @since 0.16 + */ +@ExperimentalApi +@Immutable +@AutoValue +public abstract class TimeSeries { + + TimeSeries() {} + + /** + * Creates a {@link TimeSeries}. + * + * @param labelValues the {@code LabelValue}s that uniquely identify this {@code TimeSeries}. + * @param points the data {@code Point}s of this {@code TimeSeries}. + * @param startTimestamp the start {@code Timestamp} of this {@code TimeSeries}. Must be non-null + * for cumulative {@code Point}s. + * @return a {@code TimeSeries}. + * @since 0.16 + */ + public static TimeSeries create( + List<LabelValue> labelValues, List<Point> points, @Nullable Timestamp startTimestamp) { + // Fail fast on null lists to prevent NullPointerException when copying the lists. + Utils.checkNotNull(labelValues, "labelValues"); + Utils.checkNotNull(points, "points"); + Utils.checkListElementNotNull(labelValues, "labelValue"); + Utils.checkListElementNotNull(points, "point"); + return new AutoValue_TimeSeries( + Collections.unmodifiableList(new ArrayList<LabelValue>(labelValues)), + Collections.unmodifiableList(new ArrayList<Point>(points)), + startTimestamp); + } + + /** + * Returns the set of {@link LabelValue}s that uniquely identify this {@link TimeSeries}. + * + * <p>Apply to all {@link Point}s. + * + * <p>The order of {@link LabelValue}s must match that of {@link LabelKey}s in the {@code + * MetricDescriptor}. + * + * @return the {@code LabelValue}s. + * @since 0.16 + */ + public abstract List<LabelValue> getLabelValues(); + + /** + * Returns the data {@link Point}s of this {@link TimeSeries}. + * + * @return the data {@code Point}s. + * @since 0.16 + */ + public abstract List<Point> getPoints(); + + /** + * Returns the start {@link Timestamp} of this {@link TimeSeries} if the {@link Point}s are + * cumulative, or {@code null} if the {@link Point}s are gauge. + * + * @return the start {@code Timestamp} or {@code null}. + * @since 0.16 + */ + @Nullable + public abstract Timestamp getStartTimestamp(); +} diff --git a/api/src/main/java/io/opencensus/metrics/Value.java b/api/src/main/java/io/opencensus/metrics/Value.java new file mode 100644 index 00000000..c8fe48f8 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/Value.java @@ -0,0 +1,196 @@ +/* + * 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.metrics; + +import com.google.auto.value.AutoValue; +import io.opencensus.common.ExperimentalApi; +import io.opencensus.common.Function; +import javax.annotation.concurrent.Immutable; + +/** + * The actual point value for a {@link Point}. + * + * <p>Currently there are three types of {@link Value}: + * + * <ul> + * <li>{@code double} + * <li>{@code long} + * <li>{@link Distribution} + * </ul> + * + * <p>Each {@link Point} contains exactly one of the three {@link Value} types. + * + * @since 0.16 + */ +@ExperimentalApi +@Immutable +public abstract class Value { + + Value() {} + + /** + * Returns a double {@link Value}. + * + * @param value value in double. + * @return a double {@code Value}. + * @since 0.16 + */ + public static Value doubleValue(double value) { + return ValueDouble.create(value); + } + + /** + * Returns a long {@link Value}. + * + * @param value value in long. + * @return a long {@code Value}. + * @since 0.16 + */ + public static Value longValue(long value) { + return ValueLong.create(value); + } + + /** + * Returns a {@link Distribution} {@link Value}. + * + * @param value value in {@link Distribution}. + * @return a {@code Distribution} {@code Value}. + * @since 0.16 + */ + public static Value distributionValue(Distribution value) { + return ValueDistribution.create(value); + } + + /** + * Applies the given match function to the underlying data type. + * + * @since 0.16 + */ + public abstract <T> T match( + Function<? super Double, T> doubleFunction, + Function<? super Long, T> longFunction, + Function<? super Distribution, T> distributionFunction, + Function<? super Value, T> defaultFunction); + + /** A 64-bit double-precision floating-point {@link Value}. */ + @AutoValue + @Immutable + abstract static class ValueDouble extends Value { + + ValueDouble() {} + + @Override + public final <T> T match( + Function<? super Double, T> doubleFunction, + Function<? super Long, T> longFunction, + Function<? super Distribution, T> distributionFunction, + Function<? super Value, T> defaultFunction) { + return doubleFunction.apply(getValue()); + } + + /** + * Creates a {@link ValueDouble}. + * + * @param value the value in double. + * @return a {@code ValueDouble}. + */ + static ValueDouble create(double value) { + return new AutoValue_Value_ValueDouble(value); + } + + /** + * Returns the double value. + * + * @return the double value. + */ + abstract double getValue(); + } + + /** A 64-bit integer {@link Value}. */ + @AutoValue + @Immutable + abstract static class ValueLong extends Value { + + ValueLong() {} + + @Override + public final <T> T match( + Function<? super Double, T> doubleFunction, + Function<? super Long, T> longFunction, + Function<? super Distribution, T> distributionFunction, + Function<? super Value, T> defaultFunction) { + return longFunction.apply(getValue()); + } + + /** + * Creates a {@link ValueLong}. + * + * @param value the value in long. + * @return a {@code ValueLong}. + */ + static ValueLong create(long value) { + return new AutoValue_Value_ValueLong(value); + } + + /** + * Returns the long value. + * + * @return the long value. + */ + abstract long getValue(); + } + + /** + * {@link ValueDistribution} contains summary statistics for a population of values. It optionally + * contains a histogram representing the distribution of those values across a set of buckets. + */ + @AutoValue + @Immutable + abstract static class ValueDistribution extends Value { + + ValueDistribution() {} + + @Override + public final <T> T match( + Function<? super Double, T> doubleFunction, + Function<? super Long, T> longFunction, + Function<? super Distribution, T> distributionFunction, + Function<? super Value, T> defaultFunction) { + return distributionFunction.apply(getValue()); + } + + /** + * Creates a {@link ValueDistribution}. + * + * @param value the {@link Distribution} value. + * @return a {@code ValueDistribution}. + */ + static ValueDistribution create(Distribution value) { + return new AutoValue_Value_ValueDistribution(value); + } + + /** + * Returns the {@link Distribution} value. + * + * @return the {@code Distribution} value. + */ + abstract Distribution getValue(); + } + + // TODO(songya): Add support for Summary type. + // This is an aggregation that produces percentiles directly. +} diff --git a/api/src/main/java/io/opencensus/metrics/export/ExportComponent.java b/api/src/main/java/io/opencensus/metrics/export/ExportComponent.java new file mode 100644 index 00000000..bab65d13 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/export/ExportComponent.java @@ -0,0 +1,60 @@ +/* + * 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.metrics.export; + +import io.opencensus.common.ExperimentalApi; + +/** + * Class that holds the implementation instance for {@link MetricProducerManager}. + * + * <p>Unless otherwise noted all methods (on component) results are cacheable. + * + * @since 0.16 + */ +@ExperimentalApi +public abstract class ExportComponent { + /** + * Returns the no-op implementation of the {@code ExportComponent}. + * + * @return the no-op implementation of the {@code ExportComponent}. + * @since 0.16 + */ + public static ExportComponent newNoopExportComponent() { + return new NoopExportComponent(); + } + + /** + * Returns the global {@link MetricProducerManager} which can be used to register handlers to + * export all the recorded metrics. + * + * @return the implementation of the {@code MetricExporter} or no-op if no implementation linked + * in the binary. + * @since 0.16 + */ + public abstract MetricProducerManager getMetricProducerManager(); + + private static final class NoopExportComponent extends ExportComponent { + + private static final MetricProducerManager METRIC_PRODUCER_MANAGER = + MetricProducerManager.newNoopMetricProducerManager(); + + @Override + public MetricProducerManager getMetricProducerManager() { + return METRIC_PRODUCER_MANAGER; + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/export/MetricProducerManager.java b/api/src/main/java/io/opencensus/metrics/export/MetricProducerManager.java new file mode 100644 index 00000000..c5acf174 --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/export/MetricProducerManager.java @@ -0,0 +1,89 @@ +/* + * 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.metrics.export; + +import io.opencensus.common.ExperimentalApi; +import io.opencensus.internal.Utils; +import io.opencensus.metrics.MetricProducer; +import java.util.Collections; +import java.util.Set; +import javax.annotation.concurrent.ThreadSafe; + +/** + * Keeps a set of {@link MetricProducer} that is used by exporters to determine the metrics that + * need to be exported. + * + * @since 0.16 + */ +@ExperimentalApi +@ThreadSafe +public abstract class MetricProducerManager { + + /** + * Adds the {@link MetricProducer} to the manager if it is not already present. + * + * @param metricProducer the {@code MetricProducer} to be added to the manager. + * @since 0.16 + */ + public abstract void add(MetricProducer metricProducer); + + /** + * Removes the {@link MetricProducer} to the manager if it is present. + * + * @param metricProducer the {@code MetricProducer} to be removed from the manager. + * @since 0.16 + */ + public abstract void remove(MetricProducer metricProducer); + + /** + * Returns all registered {@link MetricProducer}s that should be exported. + * + * <p>This method should be used by any metrics exporter that automatically exports data for + * {@code MetricProducer} registered with the {@code MetricProducerManager}. + * + * @return all registered {@code MetricProducer}s that should be exported. + * @since 0.16 + */ + public abstract Set<MetricProducer> getAllMetricProducer(); + + /** + * Returns a no-op implementation for {@link MetricProducerManager}. + * + * @return a no-op implementation for {@code MetricProducerManager}. + */ + static MetricProducerManager newNoopMetricProducerManager() { + return new NoopMetricProducerManager(); + } + + private static final class NoopMetricProducerManager extends MetricProducerManager { + + @Override + public void add(MetricProducer metricProducer) { + Utils.checkNotNull(metricProducer, "metricProducer"); + } + + @Override + public void remove(MetricProducer metricProducer) { + Utils.checkNotNull(metricProducer, "metricProducer"); + } + + @Override + public Set<MetricProducer> getAllMetricProducer() { + return Collections.emptySet(); + } + } +} diff --git a/api/src/main/java/io/opencensus/metrics/package-info.java b/api/src/main/java/io/opencensus/metrics/package-info.java new file mode 100644 index 00000000..33eadf0c --- /dev/null +++ b/api/src/main/java/io/opencensus/metrics/package-info.java @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * This package describes the Metrics data model. Metrics are a data model for what stats exporters + * take as input. This data model may eventually become the wire format for metrics. + * + * <p>WARNING: Currently all the public classes under this package are marked as {@link + * io.opencensus.common.ExperimentalApi}. The classes and APIs under {@link io.opencensus.metrics} + * are likely to get backwards-incompatible updates in the future. DO NOT USE except for + * experimental purposes. + * + * <p>Please see + * https://github.com/census-instrumentation/opencensus-specs/blob/master/stats/Metrics.md and + * https://github.com/census-instrumentation/opencensus-proto/blob/master/opencensus/proto/stats/metrics/metrics.proto + * for more details. + */ +@io.opencensus.common.ExperimentalApi +package io.opencensus.metrics; diff --git a/api/src/test/java/io/opencensus/metrics/DistributionTest.java b/api/src/test/java/io/opencensus/metrics/DistributionTest.java new file mode 100644 index 00000000..d511e317 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/DistributionTest.java @@ -0,0 +1,226 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.common.Timestamp; +import io.opencensus.metrics.Distribution.Bucket; +import io.opencensus.metrics.Distribution.Exemplar; +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 Value}. */ +@RunWith(JUnit4.class) +public class DistributionTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private static final Timestamp TIMESTAMP = Timestamp.create(1, 0); + private static final Map<String, String> ATTACHMENTS = Collections.singletonMap("key", "value"); + + @Test + public void createAndGet_Bucket() { + Bucket bucket = Bucket.create(98); + assertThat(bucket.getCount()).isEqualTo(98); + assertThat(bucket.getExemplar()).isNull(); + } + + @Test + public void createAndGet_BucketWithExemplar() { + Exemplar exemplar = Exemplar.create(12.2, TIMESTAMP, ATTACHMENTS); + Bucket bucket = Bucket.create(7, exemplar); + assertThat(bucket.getCount()).isEqualTo(7); + assertThat(bucket.getExemplar()).isEqualTo(exemplar); + } + + @Test + public void createBucket_preventNullExemplar() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("exemplar"); + Bucket.create(1, null); + } + + @Test + public void createAndGet_Exemplar() { + Exemplar exemplar = Exemplar.create(-9.9, TIMESTAMP, ATTACHMENTS); + assertThat(exemplar.getValue()).isEqualTo(-9.9); + assertThat(exemplar.getTimestamp()).isEqualTo(TIMESTAMP); + assertThat(exemplar.getAttachments()).isEqualTo(ATTACHMENTS); + } + + @Test + public void createAndGet_Distribution() { + Exemplar exemplar = Exemplar.create(15.0, TIMESTAMP, ATTACHMENTS); + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + List<Bucket> buckets = + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4, exemplar)); + Distribution distribution = Distribution.create(6.6, 10, 678.54, bucketBounds, buckets); + assertThat(distribution.getMean()).isEqualTo(6.6); + assertThat(distribution.getCount()).isEqualTo(10); + assertThat(distribution.getSumOfSquaredDeviations()).isEqualTo(678.54); + assertThat(distribution.getBucketBoundaries()) + .containsExactlyElementsIn(bucketBounds) + .inOrder(); + assertThat(distribution.getBuckets()).containsExactlyElementsIn(buckets).inOrder(); + } + + @Test + public void createBucket_NegativeCount() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("bucket count should be non-negative."); + Bucket.create(-5); + } + + @Test + public void createExemplar_PreventNullAttachments() { + thrown.expect(NullPointerException.class); + thrown.expectMessage("attachments"); + Exemplar.create(15, TIMESTAMP, null); + } + + @Test + public void createExemplar_PreventNullAttachmentKey() { + Map<String, String> attachments = Collections.singletonMap(null, "value"); + thrown.expect(NullPointerException.class); + thrown.expectMessage("key of attachment"); + Exemplar.create(15, TIMESTAMP, attachments); + } + + @Test + public void createExemplar_PreventNullAttachmentValue() { + Map<String, String> attachments = Collections.singletonMap("key", null); + thrown.expect(NullPointerException.class); + thrown.expectMessage("value of attachment"); + Exemplar.create(15, TIMESTAMP, attachments); + } + + @Test + public void createDistribution_NegativeCount() { + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + List<Bucket> buckets = + Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("count should be non-negative."); + Distribution.create(6.6, -10, 678.54, bucketBounds, buckets); + } + + @Test + public void createDistribution_NegativeSumOfSquaredDeviations() { + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + List<Bucket> buckets = + Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sum of squared deviations should be non-negative."); + Distribution.create(6.6, 0, -678.54, bucketBounds, buckets); + } + + @Test + public void createDistribution_ZeroCountAndPositiveMean() { + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + List<Bucket> buckets = + Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("mean should be 0 if count is 0."); + Distribution.create(6.6, 0, 0, bucketBounds, buckets); + } + + @Test + public void createDistribution_ZeroCountAndSumOfSquaredDeviations() { + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + List<Bucket> buckets = + Arrays.asList(Bucket.create(0), Bucket.create(0), Bucket.create(0), Bucket.create(0)); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("sum of squared deviations should be 0 if count is 0."); + Distribution.create(0, 0, 678.54, bucketBounds, buckets); + } + + @Test + public void createDistribution_NullBucketBounds() { + List<Bucket> buckets = + Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); + thrown.expect(NullPointerException.class); + thrown.expectMessage("bucketBoundaries list should not be null."); + Distribution.create(6.6, 10, 678.54, null, buckets); + } + + @Test + public void createDistribution_UnorderedBucketBounds() { + List<Double> bucketBounds = Arrays.asList(0.0, -1.0, 1.0); + List<Bucket> buckets = + Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("bucket boundaries not sorted."); + Distribution.create(6.6, 10, 678.54, bucketBounds, buckets); + } + + @Test + public void createDistribution_NullBucketList() { + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + thrown.expect(NullPointerException.class); + thrown.expectMessage("bucket list should not be null."); + Distribution.create(6.6, 10, 678.54, bucketBounds, null); + } + + @Test + public void createDistribution_NullBucket() { + List<Double> bucketBounds = Arrays.asList(-1.0, 0.0, 1.0); + List<Bucket> buckets = + Arrays.asList(Bucket.create(3), Bucket.create(1), null, Bucket.create(4)); + thrown.expect(NullPointerException.class); + thrown.expectMessage("bucket should not be null."); + Distribution.create(6.6, 10, 678.54, bucketBounds, buckets); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + Distribution.create( + 10, + 10, + 1, + Arrays.asList(-5.0, 0.0, 5.0), + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))), + Distribution.create( + 10, + 10, + 1, + Arrays.asList(-5.0, 0.0, 5.0), + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))) + .addEqualityGroup( + Distribution.create( + -7, + 10, + 23.456, + Arrays.asList(-5.0, 0.0, 5.0), + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/LabelKeyTest.java b/api/src/test/java/io/opencensus/metrics/LabelKeyTest.java new file mode 100644 index 00000000..83f2b59a --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/LabelKeyTest.java @@ -0,0 +1,86 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link LabelKey}. */ +@RunWith(JUnit4.class) +public class LabelKeyTest { + + private static final LabelKey KEY = LabelKey.create("key", "description"); + + @Test + public void testGetKey() { + assertThat(KEY.getKey()).isEqualTo("key"); + } + + @Test + public void testGetDescription() { + assertThat(KEY.getDescription()).isEqualTo("description"); + } + + @Test + public void create_NoLengthConstraint() { + // We have a length constraint of 256-characters for TagKey. That constraint doesn't apply to + // LabelKey. + char[] chars = new char[300]; + Arrays.fill(chars, 'k'); + String key = new String(chars); + assertThat(LabelKey.create(key, "").getKey()).isEqualTo(key); + } + + @Test + public void create_WithUnprintableChars() { + String key = "\2ab\3cd"; + String description = "\4ef\5gh"; + LabelKey labelKey = LabelKey.create(key, description); + assertThat(labelKey.getKey()).isEqualTo(key); + assertThat(labelKey.getDescription()).isEqualTo(description); + } + + @Test + public void create_WithNonAsciiChars() { + String key = "键"; + String description = "测试用键"; + LabelKey nonAsciiKey = LabelKey.create(key, description); + assertThat(nonAsciiKey.getKey()).isEqualTo(key); + assertThat(nonAsciiKey.getDescription()).isEqualTo(description); + } + + @Test + public void create_Empty() { + LabelKey emptyKey = LabelKey.create("", ""); + assertThat(emptyKey.getKey()).isEmpty(); + assertThat(emptyKey.getDescription()).isEmpty(); + } + + @Test + public void testLabelKeyEquals() { + new EqualsTester() + .addEqualityGroup(LabelKey.create("foo", ""), LabelKey.create("foo", "")) + .addEqualityGroup(LabelKey.create("foo", "description")) + .addEqualityGroup(LabelKey.create("bar", "")) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/LabelValueTest.java b/api/src/test/java/io/opencensus/metrics/LabelValueTest.java new file mode 100644 index 00000000..e5526b2f --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/LabelValueTest.java @@ -0,0 +1,74 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link LabelValue}. */ +@RunWith(JUnit4.class) +public class LabelValueTest { + + private static final LabelValue VALUE = LabelValue.create("value"); + private static final LabelValue UNSET = LabelValue.create(null); + private static final LabelValue EMPTY = LabelValue.create(""); + + @Test + public void testGetValue() { + assertThat(VALUE.getValue()).isEqualTo("value"); + assertThat(UNSET.getValue()).isNull(); + assertThat(EMPTY.getValue()).isEmpty(); + } + + @Test + public void create_NoLengthConstraint() { + // We have a length constraint of 256-characters for TagValue. That constraint doesn't apply to + // LabelValue. + char[] chars = new char[300]; + Arrays.fill(chars, 'v'); + String value = new String(chars); + assertThat(LabelValue.create(value).getValue()).isEqualTo(value); + } + + @Test + public void create_WithUnprintableChars() { + String value = "\2ab\3cd"; + assertThat(LabelValue.create(value).getValue()).isEqualTo(value); + } + + @Test + public void create_WithNonAsciiChars() { + String value = "值"; + LabelValue nonAsciiValue = LabelValue.create(value); + assertThat(nonAsciiValue.getValue()).isEqualTo(value); + } + + @Test + public void testLabelValueEquals() { + new EqualsTester() + .addEqualityGroup(LabelValue.create("foo"), LabelValue.create("foo")) + .addEqualityGroup(UNSET) + .addEqualityGroup(EMPTY) + .addEqualityGroup(LabelValue.create("bar")) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/MetricDescriptorTest.java b/api/src/test/java/io/opencensus/metrics/MetricDescriptorTest.java new file mode 100644 index 00000000..9c0a42fc --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/MetricDescriptorTest.java @@ -0,0 +1,102 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.metrics.MetricDescriptor.Type; +import java.util.Arrays; +import java.util.List; +import org.hamcrest.CoreMatchers; +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 MetricDescriptor}. */ +@RunWith(JUnit4.class) +public class MetricDescriptorTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private static final String METRIC_NAME_1 = "metric1"; + private static final String METRIC_NAME_2 = "metric2"; + private static final String DESCRIPTION = "Metric description."; + private static final String UNIT = "kb/s"; + private static final LabelKey KEY_1 = LabelKey.create("key1", "some key"); + private static final LabelKey KEY_2 = LabelKey.create("key2", "some other key"); + + @Test + public void testGet() { + MetricDescriptor metricDescriptor = + MetricDescriptor.create( + METRIC_NAME_1, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, Arrays.asList(KEY_1, KEY_2)); + assertThat(metricDescriptor.getName()).isEqualTo(METRIC_NAME_1); + assertThat(metricDescriptor.getDescription()).isEqualTo(DESCRIPTION); + assertThat(metricDescriptor.getUnit()).isEqualTo(UNIT); + assertThat(metricDescriptor.getType()).isEqualTo(Type.GAUGE_DOUBLE); + assertThat(metricDescriptor.getLabelKeys()).containsExactly(KEY_1, KEY_2).inOrder(); + } + + @Test + public void preventNullLabelKeyList() { + thrown.expect(NullPointerException.class); + thrown.expectMessage(CoreMatchers.equalTo("labelKeys")); + MetricDescriptor.create(METRIC_NAME_1, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, null); + } + + @Test + public void preventNullLabelKey() { + List<LabelKey> keys = Arrays.asList(KEY_1, null); + thrown.expect(NullPointerException.class); + thrown.expectMessage(CoreMatchers.equalTo("labelKey")); + MetricDescriptor.create(METRIC_NAME_1, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, keys); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + MetricDescriptor.create( + METRIC_NAME_1, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, Arrays.asList(KEY_1, KEY_2)), + MetricDescriptor.create( + METRIC_NAME_1, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, Arrays.asList(KEY_1, KEY_2))) + .addEqualityGroup( + MetricDescriptor.create( + METRIC_NAME_2, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, Arrays.asList(KEY_1, KEY_2))) + .addEqualityGroup( + MetricDescriptor.create( + METRIC_NAME_2, DESCRIPTION, UNIT, Type.GAUGE_INT64, Arrays.asList(KEY_1, KEY_2))) + .addEqualityGroup( + MetricDescriptor.create( + METRIC_NAME_1, + DESCRIPTION, + UNIT, + Type.CUMULATIVE_DISTRIBUTION, + Arrays.asList(KEY_1, KEY_2))) + .addEqualityGroup( + MetricDescriptor.create( + METRIC_NAME_1, + DESCRIPTION, + UNIT, + Type.CUMULATIVE_DISTRIBUTION, + Arrays.asList(KEY_1))) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/MetricRegistryTest.java b/api/src/test/java/io/opencensus/metrics/MetricRegistryTest.java new file mode 100644 index 00000000..49e8ce02 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/MetricRegistryTest.java @@ -0,0 +1,185 @@ +/* + * 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.metrics; + +import io.opencensus.common.ToDoubleFunction; +import io.opencensus.common.ToLongFunction; +import java.util.LinkedHashMap; +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 MetricRegistry}. */ +@RunWith(JUnit4.class) +public class MetricRegistryTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + private final MetricRegistry metricRegistry = + MetricsComponent.newNoopMetricsComponent().getMetricRegistry(); + + @Test + public void addDoubleGauge_NullName() { + thrown.expect(NullPointerException.class); + metricRegistry.addDoubleGauge( + null, + "description", + "1", + new LinkedHashMap<LabelKey, LabelValue>(), + null, + new ToDoubleFunction<Object>() { + @Override + public double applyAsDouble(Object value) { + return 5.0; + } + }); + } + + @Test + public void addDoubleGauge_NullDescription() { + thrown.expect(NullPointerException.class); + metricRegistry.addDoubleGauge( + "name", + null, + "1", + new LinkedHashMap<LabelKey, LabelValue>(), + null, + new ToDoubleFunction<Object>() { + @Override + public double applyAsDouble(Object value) { + return 5.0; + } + }); + } + + @Test + public void addDoubleGauge_NullUnit() { + thrown.expect(NullPointerException.class); + metricRegistry.addDoubleGauge( + "name", + "description", + null, + new LinkedHashMap<LabelKey, LabelValue>(), + null, + new ToDoubleFunction<Object>() { + @Override + public double applyAsDouble(Object value) { + return 5.0; + } + }); + } + + @Test + public void addDoubleGauge_NullLabels() { + thrown.expect(NullPointerException.class); + metricRegistry.addDoubleGauge( + "name", + "description", + "1", + null, + null, + new ToDoubleFunction<Object>() { + @Override + public double applyAsDouble(Object value) { + return 5.0; + } + }); + } + + @Test + public void addDoubleGauge_NullFunction() { + thrown.expect(NullPointerException.class); + metricRegistry.addDoubleGauge( + "name", "description", "1", new LinkedHashMap<LabelKey, LabelValue>(), null, null); + } + + @Test + public void addLongGauge_NullName() { + thrown.expect(NullPointerException.class); + metricRegistry.addLongGauge( + null, + "description", + "1", + new LinkedHashMap<LabelKey, LabelValue>(), + null, + new ToLongFunction<Object>() { + @Override + public long applyAsLong(Object value) { + return 5; + } + }); + } + + @Test + public void addLongGauge_NullDescription() { + thrown.expect(NullPointerException.class); + metricRegistry.addLongGauge( + "name", + null, + "1", + new LinkedHashMap<LabelKey, LabelValue>(), + null, + new ToLongFunction<Object>() { + @Override + public long applyAsLong(Object value) { + return 5; + } + }); + } + + @Test + public void addLongGauge_NullUnit() { + thrown.expect(NullPointerException.class); + metricRegistry.addLongGauge( + "name", + "description", + null, + new LinkedHashMap<LabelKey, LabelValue>(), + null, + new ToLongFunction<Object>() { + @Override + public long applyAsLong(Object value) { + return 5; + } + }); + } + + @Test + public void addLongGauge_NullLabels() { + thrown.expect(NullPointerException.class); + metricRegistry.addLongGauge( + "name", + "description", + "1", + null, + null, + new ToLongFunction<Object>() { + @Override + public long applyAsLong(Object value) { + return 5; + } + }); + } + + @Test + public void addLongGauge_NullFunction() { + thrown.expect(NullPointerException.class); + metricRegistry.addLongGauge( + "name", "description", "1", new LinkedHashMap<LabelKey, LabelValue>(), null, null); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/MetricTest.java b/api/src/test/java/io/opencensus/metrics/MetricTest.java new file mode 100644 index 00000000..37deed4b --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/MetricTest.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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.common.Timestamp; +import io.opencensus.metrics.MetricDescriptor.Type; +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 Metric}. */ +@RunWith(JUnit4.class) +public class MetricTest { + + @Rule public final ExpectedException thrown = ExpectedException.none(); + + private static final String METRIC_NAME_1 = "metric1"; + private static final String METRIC_NAME_2 = "metric2"; + private static final String DESCRIPTION = "Metric description."; + private static final String UNIT = "kb/s"; + private static final LabelKey KEY_1 = LabelKey.create("key1", "some key"); + private static final LabelKey KEY_2 = LabelKey.create("key1", "some other key"); + private static final MetricDescriptor METRIC_DESCRIPTOR_1 = + MetricDescriptor.create( + METRIC_NAME_1, DESCRIPTION, UNIT, Type.GAUGE_DOUBLE, Arrays.asList(KEY_1, KEY_2)); + private static final MetricDescriptor METRIC_DESCRIPTOR_2 = + MetricDescriptor.create( + METRIC_NAME_2, DESCRIPTION, UNIT, Type.CUMULATIVE_INT64, Arrays.asList(KEY_1)); + private static final LabelValue LABEL_VALUE_1 = LabelValue.create("value1"); + private static final LabelValue LABEL_VALUE_2 = LabelValue.create("value1"); + private static final LabelValue LABEL_VALUE_EMPTY = LabelValue.create(""); + private static final Value VALUE_LONG = Value.longValue(12345678); + private static final Value VALUE_DOUBLE_1 = Value.doubleValue(-345.77); + private static final Value VALUE_DOUBLE_2 = Value.doubleValue(133.79); + private static final Timestamp TIMESTAMP_1 = Timestamp.fromMillis(1000); + private static final Timestamp TIMESTAMP_2 = Timestamp.fromMillis(2000); + private static final Timestamp TIMESTAMP_3 = Timestamp.fromMillis(3000); + private static final Point POINT_1 = Point.create(VALUE_DOUBLE_1, TIMESTAMP_2); + private static final Point POINT_2 = Point.create(VALUE_DOUBLE_2, TIMESTAMP_3); + private static final Point POINT_3 = Point.create(VALUE_LONG, TIMESTAMP_3); + private static final TimeSeries GAUGE_TIME_SERIES_1 = + TimeSeries.create(Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), null); + private static final TimeSeries GAUGE_TIME_SERIES_2 = + TimeSeries.create(Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_2), null); + private static final TimeSeries CUMULATIVE_TIME_SERIES = + TimeSeries.create(Arrays.asList(LABEL_VALUE_EMPTY), Arrays.asList(POINT_3), TIMESTAMP_1); + + @Test + public void testGet() { + Metric metric = + Metric.create(METRIC_DESCRIPTOR_1, Arrays.asList(GAUGE_TIME_SERIES_1, GAUGE_TIME_SERIES_2)); + assertThat(metric.getMetricDescriptor()).isEqualTo(METRIC_DESCRIPTOR_1); + assertThat(metric.getTimeSeriesList()) + .containsExactly(GAUGE_TIME_SERIES_1, GAUGE_TIME_SERIES_2) + .inOrder(); + } + + @Test + public void typeMismatch_GaugeDouble_Long() { + typeMismatch( + METRIC_DESCRIPTOR_1, + Arrays.asList(CUMULATIVE_TIME_SERIES), + String.format("Type mismatch: %s, %s.", Type.GAUGE_DOUBLE, "ValueLong")); + } + + @Test + public void typeMismatch_CumulativeInt64_Double() { + typeMismatch( + METRIC_DESCRIPTOR_2, + Arrays.asList(GAUGE_TIME_SERIES_1), + String.format("Type mismatch: %s, %s.", Type.CUMULATIVE_INT64, "ValueDouble")); + } + + private void typeMismatch( + MetricDescriptor metricDescriptor, List<TimeSeries> timeSeriesList, String errorMessage) { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(errorMessage); + Metric.create(metricDescriptor, timeSeriesList); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + Metric.create( + METRIC_DESCRIPTOR_1, Arrays.asList(GAUGE_TIME_SERIES_1, GAUGE_TIME_SERIES_2)), + Metric.create( + METRIC_DESCRIPTOR_1, Arrays.asList(GAUGE_TIME_SERIES_1, GAUGE_TIME_SERIES_2))) + .addEqualityGroup(Metric.create(METRIC_DESCRIPTOR_1, Collections.<TimeSeries>emptyList())) + .addEqualityGroup(Metric.create(METRIC_DESCRIPTOR_2, Arrays.asList(CUMULATIVE_TIME_SERIES))) + .addEqualityGroup(Metric.create(METRIC_DESCRIPTOR_2, Collections.<TimeSeries>emptyList())) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/MetricsComponentTest.java b/api/src/test/java/io/opencensus/metrics/MetricsComponentTest.java new file mode 100644 index 00000000..1c4e70f7 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/MetricsComponentTest.java @@ -0,0 +1,40 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.metrics.export.ExportComponent; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link MetricsComponent}. */ +@RunWith(JUnit4.class) +public class MetricsComponentTest { + @Test + public void defaultExportComponent() { + assertThat(MetricsComponent.newNoopMetricsComponent().getExportComponent()) + .isInstanceOf(ExportComponent.newNoopExportComponent().getClass()); + } + + @Test + public void defaultMetricRegistry() { + assertThat(MetricsComponent.newNoopMetricsComponent().getMetricRegistry()) + .isInstanceOf(MetricRegistry.newNoopMetricRegistry().getClass()); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/MetricsTest.java b/api/src/test/java/io/opencensus/metrics/MetricsTest.java new file mode 100644 index 00000000..9e0eee1f --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/MetricsTest.java @@ -0,0 +1,71 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.metrics.export.ExportComponent; +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 Metrics}. */ +@RunWith(JUnit4.class) +public class MetricsTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void loadMetricsComponent_UsesProvidedClassLoader() { + final RuntimeException toThrow = new RuntimeException("UseClassLoader"); + thrown.expect(RuntimeException.class); + thrown.expectMessage("UseClassLoader"); + Metrics.loadMetricsComponent( + new ClassLoader() { + @Override + public Class<?> loadClass(String name) { + throw toThrow; + } + }); + } + + @Test + public void loadMetricsComponent_IgnoresMissingClasses() { + ClassLoader classLoader = + new ClassLoader() { + @Override + public Class<?> loadClass(String name) throws ClassNotFoundException { + throw new ClassNotFoundException(); + } + }; + assertThat(Metrics.loadMetricsComponent(classLoader).getClass().getName()) + .isEqualTo("io.opencensus.metrics.MetricsComponent$NoopMetricsComponent"); + } + + @Test + public void defaultExportComponent() { + assertThat(Metrics.getExportComponent()) + .isInstanceOf(ExportComponent.newNoopExportComponent().getClass()); + } + + @Test + public void defaultMetricRegistry() { + assertThat(Metrics.getMetricRegistry()) + .isInstanceOf(MetricRegistry.newNoopMetricRegistry().getClass()); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/PointTest.java b/api/src/test/java/io/opencensus/metrics/PointTest.java new file mode 100644 index 00000000..cb6175c1 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/PointTest.java @@ -0,0 +1,68 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.common.Timestamp; +import io.opencensus.metrics.Distribution.Bucket; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link Point}. */ +@RunWith(JUnit4.class) +public class PointTest { + + private static final Value DOUBLE_VALUE = Value.doubleValue(55.5); + private static final Value LONG_VALUE = Value.longValue(9876543210L); + private static final Value DISTRIBUTION_VALUE = + Value.distributionValue( + Distribution.create( + 6.6, + 10, + 678.54, + Arrays.asList(-1.0, 0.0, 1.0), + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4)))); + private static final Timestamp TIMESTAMP_1 = Timestamp.create(1, 2); + private static final Timestamp TIMESTAMP_2 = Timestamp.create(3, 4); + private static final Timestamp TIMESTAMP_3 = Timestamp.create(5, 6); + + @Test + public void testGet() { + Point point = Point.create(DOUBLE_VALUE, TIMESTAMP_1); + assertThat(point.getValue()).isEqualTo(DOUBLE_VALUE); + assertThat(point.getTimestamp()).isEqualTo(TIMESTAMP_1); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + Point.create(DOUBLE_VALUE, TIMESTAMP_1), Point.create(DOUBLE_VALUE, TIMESTAMP_1)) + .addEqualityGroup(Point.create(LONG_VALUE, TIMESTAMP_1)) + .addEqualityGroup(Point.create(LONG_VALUE, TIMESTAMP_2)) + .addEqualityGroup( + Point.create(DISTRIBUTION_VALUE, TIMESTAMP_2), + Point.create(DISTRIBUTION_VALUE, TIMESTAMP_2)) + .addEqualityGroup(Point.create(DISTRIBUTION_VALUE, TIMESTAMP_3)) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/TimeSeriesTest.java b/api/src/test/java/io/opencensus/metrics/TimeSeriesTest.java new file mode 100644 index 00000000..07dff97d --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/TimeSeriesTest.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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.common.Timestamp; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.hamcrest.CoreMatchers; +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 TimeSeries}. */ +@RunWith(JUnit4.class) +public class TimeSeriesTest { + + @Rule public ExpectedException thrown = ExpectedException.none(); + + private static final LabelValue LABEL_VALUE_1 = LabelValue.create("value1"); + private static final LabelValue LABEL_VALUE_2 = LabelValue.create("value2"); + private static final Value VALUE_LONG = Value.longValue(12345678); + private static final Value VALUE_DOUBLE = Value.doubleValue(-345.77); + private static final Timestamp TIMESTAMP_1 = Timestamp.fromMillis(1000); + private static final Timestamp TIMESTAMP_2 = Timestamp.fromMillis(2000); + private static final Timestamp TIMESTAMP_3 = Timestamp.fromMillis(3000); + private static final Point POINT_1 = Point.create(VALUE_DOUBLE, TIMESTAMP_2); + private static final Point POINT_2 = Point.create(VALUE_LONG, TIMESTAMP_3); + + @Test + public void testGet_TimeSeries() { + TimeSeries cumulativeTimeSeries = + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), TIMESTAMP_1); + assertThat(cumulativeTimeSeries.getStartTimestamp()).isEqualTo(TIMESTAMP_1); + assertThat(cumulativeTimeSeries.getLabelValues()) + .containsExactly(LABEL_VALUE_1, LABEL_VALUE_2) + .inOrder(); + assertThat(cumulativeTimeSeries.getPoints()).containsExactly(POINT_1).inOrder(); + } + + @Test + public void create_WithNullLabelValueList() { + thrown.expect(NullPointerException.class); + thrown.expectMessage(CoreMatchers.equalTo("labelValues")); + TimeSeries.create(null, Collections.<Point>emptyList(), TIMESTAMP_1); + } + + @Test + public void create_WithNullLabelValue() { + List<LabelValue> labelValues = Arrays.asList(LABEL_VALUE_1, null); + thrown.expect(NullPointerException.class); + thrown.expectMessage(CoreMatchers.equalTo("labelValue")); + TimeSeries.create(labelValues, Collections.<Point>emptyList(), TIMESTAMP_1); + } + + @Test + public void create_WithNullPointList() { + thrown.expect(NullPointerException.class); + thrown.expectMessage(CoreMatchers.equalTo("points")); + TimeSeries.create(Collections.<LabelValue>emptyList(), null, TIMESTAMP_1); + } + + @Test + public void create_WithNullPoint() { + List<Point> points = Arrays.asList(POINT_1, null); + thrown.expect(NullPointerException.class); + thrown.expectMessage(CoreMatchers.equalTo("point")); + TimeSeries.create(Collections.<LabelValue>emptyList(), points, TIMESTAMP_1); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup( + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), TIMESTAMP_1), + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), TIMESTAMP_1)) + .addEqualityGroup( + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), null), + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), null)) + .addEqualityGroup( + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1, LABEL_VALUE_2), Arrays.asList(POINT_1), TIMESTAMP_2)) + .addEqualityGroup( + TimeSeries.create(Arrays.asList(LABEL_VALUE_1), Arrays.asList(POINT_1), TIMESTAMP_2)) + .addEqualityGroup( + TimeSeries.create(Arrays.asList(LABEL_VALUE_1), Arrays.asList(POINT_2), TIMESTAMP_2)) + .addEqualityGroup( + TimeSeries.create( + Arrays.asList(LABEL_VALUE_1), Arrays.asList(POINT_1, POINT_2), TIMESTAMP_2)) + .testEquals(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/ValueTest.java b/api/src/test/java/io/opencensus/metrics/ValueTest.java new file mode 100644 index 00000000..63430b28 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/ValueTest.java @@ -0,0 +1,128 @@ +/* + * 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.metrics; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.common.testing.EqualsTester; +import io.opencensus.common.Function; +import io.opencensus.common.Functions; +import io.opencensus.metrics.Distribution.Bucket; +import io.opencensus.metrics.Value.ValueDistribution; +import io.opencensus.metrics.Value.ValueDouble; +import io.opencensus.metrics.Value.ValueLong; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Unit tests for {@link Value}. */ +@RunWith(JUnit4.class) +public class ValueTest { + + private static final Distribution DISTRIBUTION = + Distribution.create( + 10, + 10, + 1, + Arrays.asList(-5.0, 0.0, 5.0), + Arrays.asList(Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))); + + @Test + public void createAndGet_ValueDouble() { + Value value = Value.doubleValue(-34.56); + assertThat(value).isInstanceOf(ValueDouble.class); + assertThat(((ValueDouble) value).getValue()).isEqualTo(-34.56); + } + + @Test + public void createAndGet_ValueLong() { + Value value = Value.longValue(123456789); + assertThat(value).isInstanceOf(ValueLong.class); + assertThat(((ValueLong) value).getValue()).isEqualTo(123456789); + } + + @Test + public void createAndGet_ValueDistribution() { + Value value = Value.distributionValue(DISTRIBUTION); + assertThat(value).isInstanceOf(ValueDistribution.class); + assertThat(((ValueDistribution) value).getValue()).isEqualTo(DISTRIBUTION); + } + + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup(Value.doubleValue(1.0), Value.doubleValue(1.0)) + .addEqualityGroup(Value.doubleValue(2.0)) + .addEqualityGroup(Value.longValue(1L)) + .addEqualityGroup(Value.longValue(2L)) + .addEqualityGroup( + Value.distributionValue( + Distribution.create( + -7, + 10, + 23.456, + Arrays.asList(-5.0, 0.0, 5.0), + Arrays.asList( + Bucket.create(3), Bucket.create(1), Bucket.create(2), Bucket.create(4))))) + .testEquals(); + } + + @Test + public void testMatch() { + List<Value> values = + Arrays.asList( + ValueDouble.create(1.0), ValueLong.create(-1), ValueDistribution.create(DISTRIBUTION)); + List<Number> expected = + Arrays.<Number>asList(1.0, -1L, 10.0, 10L, 1.0, -5.0, 0.0, 5.0, 3L, 1L, 2L, 4L); + final List<Number> actual = new ArrayList<Number>(); + for (Value value : values) { + value.match( + new Function<Double, Object>() { + @Override + public Object apply(Double arg) { + actual.add(arg); + return null; + } + }, + new Function<Long, Object>() { + @Override + public Object apply(Long arg) { + actual.add(arg); + return null; + } + }, + new Function<Distribution, Object>() { + @Override + public Object apply(Distribution arg) { + actual.add(arg.getMean()); + actual.add(arg.getCount()); + actual.add(arg.getSumOfSquaredDeviations()); + actual.addAll(arg.getBucketBoundaries()); + for (Bucket bucket : arg.getBuckets()) { + actual.add(bucket.getCount()); + } + return null; + } + }, + Functions.throwAssertionError()); + } + assertThat(actual).containsExactlyElementsIn(expected).inOrder(); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/export/ExportComponentTest.java b/api/src/test/java/io/opencensus/metrics/export/ExportComponentTest.java new file mode 100644 index 00000000..15c6e883 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/export/ExportComponentTest.java @@ -0,0 +1,33 @@ +/* + * 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.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 ExportComponent}. */ +@RunWith(JUnit4.class) +public class ExportComponentTest { + @Test + public void defaultMetricExporter() { + assertThat(ExportComponent.newNoopExportComponent().getMetricProducerManager()) + .isInstanceOf(MetricProducerManager.class); + } +} diff --git a/api/src/test/java/io/opencensus/metrics/export/MetricProducerManagerTest.java b/api/src/test/java/io/opencensus/metrics/export/MetricProducerManagerTest.java new file mode 100644 index 00000000..07854927 --- /dev/null +++ b/api/src/test/java/io/opencensus/metrics/export/MetricProducerManagerTest.java @@ -0,0 +1,81 @@ +/* + * 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.metrics.export; + +import static com.google.common.truth.Truth.assertThat; + +import io.opencensus.metrics.MetricProducer; +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 MetricProducerManager}. */ +@RunWith(JUnit4.class) +public class MetricProducerManagerTest { + private final MetricProducerManager metricProducerManager = + MetricProducerManager.newNoopMetricProducerManager(); + @Mock private MetricProducer metricProducer; + + @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()).isEmpty(); + } + + @Test + public void addAndRemove() { + metricProducerManager.add(metricProducer); + assertThat(metricProducerManager.getAllMetricProducer()).isEmpty(); + 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 getAllMetricProducer_empty() { + assertThat(metricProducerManager.getAllMetricProducer()).isEmpty(); + } +} |