diff --git a/java/google/registry/monitoring/metrics/contrib/AbstractMetricSubject.java b/java/google/registry/monitoring/metrics/contrib/AbstractMetricSubject.java
new file mode 100644
index 000000000..e6a893e40
--- /dev/null
+++ b/java/google/registry/monitoring/metrics/contrib/AbstractMetricSubject.java
@@ -0,0 +1,210 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// 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 google.registry.monitoring.metrics.contrib;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Ordering;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import google.registry.monitoring.metrics.Metric;
+import google.registry.monitoring.metrics.MetricPoint;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Nullable;
+
+/**
+ * Base truth subject for asserting things about {@link Metric} instances.
+ *
+ *
For use with the Google Truth framework.
+ */
+abstract class AbstractMetricSubject<
+ T, M extends Metric, S extends AbstractMetricSubject>
+ extends Subject {
+
+ /** And chainer to allow fluent assertions. */
+ public static class And> {
+
+ private final S subject;
+
+ And(S subject) {
+ this.subject = subject;
+ }
+
+ public S and() {
+ return subject;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ And andChainer() {
+ return new And<>((S) this);
+ }
+
+ /** List of label value tuples about which an assertion has been made so far.
+ *
+ * Used to track what tuples have been seen, in order to support hasNoOtherValues() assertions.
+ */
+ protected final Set> expectedNondefaultLabelTuples = new HashSet<>();
+
+ /**
+ * Function to convert a metric point to a nice string representation for use in error messages.
+ */
+ protected final Function, String> metricPointConverter =
+ new Function, String>() {
+ @Override
+ public String apply(MetricPoint metricPoint) {
+ return String.format(
+ "%s => %s",
+ Joiner.on(':').join(metricPoint.labelValues()),
+ getMessageRepresentation(metricPoint.value()));
+ }
+ };
+
+ protected AbstractMetricSubject(FailureStrategy strategy, M actual) {
+ super(strategy, checkNotNull(actual));
+ }
+
+ /**
+ * Returns the string representation of the subject.
+ *
+ * For metrics, it makes sense to use the metric name, as given in the schema.
+ */
+ @Override
+ public String actualCustomStringRepresentation() {
+ return actual().getMetricSchema().name();
+ }
+
+ /**
+ * Asserts that the metric has a given value for the specified label values.
+ *
+ * @param value the value which the metric should have
+ * @param labels the labels for which the value is being asserted; the number and order of labels
+ * should match the definition of the metric
+ */
+ public And hasValueForLabels(T value, String... labels) {
+ MetricPoint metricPoint = findMetricPointForLabels(ImmutableList.copyOf(labels));
+ if (metricPoint == null) {
+ failWithBadResults(
+ "has a value for labels",
+ Joiner.on(':').join(labels),
+ "has labeled values",
+ Lists.transform(
+ Ordering.>natural().sortedCopy(actual().getTimestampedValues()),
+ metricPointConverter));
+ }
+ if (!metricPoint.value().equals(value)) {
+ failWithBadResults(
+ String.format("has a value of %s for labels", value),
+ Joiner.on(':').join(labels),
+ "has a value of",
+ getMessageRepresentation(metricPoint.value()));
+ }
+ expectedNondefaultLabelTuples.add(ImmutableList.copyOf(labels));
+ return andChainer();
+ }
+
+ /**
+ * Asserts that the metric has any (non-default) value for the specified label values.
+ *
+ * @param labels the labels for which the value is being asserted; the number and order of labels
+ * should match the definition of the metric
+ */
+ public And hasAnyValueForLabels(String... labels) {
+ MetricPoint metricPoint = findMetricPointForLabels(ImmutableList.copyOf(labels));
+ if (metricPoint == null) {
+ failWithBadResults(
+ "has a value for labels",
+ Joiner.on(':').join(labels),
+ "has labeled values",
+ Lists.transform(
+ Ordering.>natural().sortedCopy(actual().getTimestampedValues()),
+ metricPointConverter));
+ }
+ if (hasDefaultValue(metricPoint)) {
+ failWithBadResults(
+ "has a non-default value for labels",
+ Joiner.on(':').join(labels),
+ "has a value of",
+ getMessageRepresentation(metricPoint.value()));
+ }
+ expectedNondefaultLabelTuples.add(ImmutableList.copyOf(labels));
+ return andChainer();
+ }
+
+ /**
+ * Asserts that the metric does not have a (non-default) value for the specified label values.
+ */
+ protected And doesNotHaveAnyValueForLabels(String... labels) {
+ MetricPoint metricPoint = findMetricPointForLabels(ImmutableList.copyOf(labels));
+ if (metricPoint != null) {
+ failWithBadResults(
+ "has no value for labels",
+ Joiner.on(':').join(labels),
+ "has a value of",
+ getMessageRepresentation(metricPoint.value()));
+ }
+ return andChainer();
+ }
+
+ /**
+ * Asserts that the metric has no (non-default) values other than those about which an assertion
+ * has already been made.
+ */
+ public And hasNoOtherValues() {
+ for (MetricPoint metricPoint : actual().getTimestampedValues()) {
+ if (!expectedNondefaultLabelTuples.contains(metricPoint.labelValues())) {
+ if (!hasDefaultValue(metricPoint)) {
+ failWithBadResults(
+ "has",
+ "no other nondefault values",
+ "has labeled values",
+ Lists.transform(
+ Ordering.>natural().sortedCopy(actual().getTimestampedValues()),
+ metricPointConverter));
+ }
+ return andChainer();
+ }
+ }
+ return andChainer();
+ }
+
+ private @Nullable MetricPoint findMetricPointForLabels(ImmutableList labels) {
+ if (actual().getMetricSchema().labels().size() != labels.size()) {
+ return null;
+ }
+ for (MetricPoint metricPoint : actual().getTimestampedValues()) {
+ if (metricPoint.labelValues().equals(labels)) {
+ return metricPoint;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the metric point has a non-default value.
+ *
+ * This should be overridden by subclasses. E.g. for incrementable metrics, the method should
+ * return true if the value is not zero, and so on.
+ */
+ protected abstract boolean hasDefaultValue(MetricPoint metricPoint);
+
+ /** Returns a string representation of a metric point value, for use in error messages. */
+ protected abstract String getMessageRepresentation(T value);
+}
diff --git a/java/google/registry/monitoring/metrics/contrib/BUILD b/java/google/registry/monitoring/metrics/contrib/BUILD
new file mode 100644
index 000000000..e646eaf42
--- /dev/null
+++ b/java/google/registry/monitoring/metrics/contrib/BUILD
@@ -0,0 +1,17 @@
+package(
+ default_testonly = 1,
+ default_visibility = ["//visibility:public"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+java_library(
+ name = "contrib",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/google/registry/monitoring/metrics",
+ "@com_google_code_findbugs_jsr305",
+ "@com_google_guava",
+ "@com_google_truth",
+ ],
+)
diff --git a/java/google/registry/monitoring/metrics/contrib/EventMetricSubject.java b/java/google/registry/monitoring/metrics/contrib/EventMetricSubject.java
new file mode 100644
index 000000000..937ac09e6
--- /dev/null
+++ b/java/google/registry/monitoring/metrics/contrib/EventMetricSubject.java
@@ -0,0 +1,109 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// 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 google.registry.monitoring.metrics.contrib;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.Range;
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
+import google.registry.monitoring.metrics.Distribution;
+import google.registry.monitoring.metrics.EventMetric;
+import google.registry.monitoring.metrics.MetricPoint;
+import java.util.Map;
+import javax.annotation.Nullable;
+
+/**
+ * Truth subject for the {@link EventMetric} class.
+ *
+ * For use with the Google Truth framework. Usage:
+ *
+ *
assertThat(myEventMetric)
+ * .hasAnyValueForLabels("label1", "label2", "label3")
+ * .and()
+ * .hasNoOtherValues();
+ * assertThat(myEventMetric)
+ * .doesNotHaveAnyValueForLabels("label1", "label2");
+ *
+ *
+ * The assertions treat an empty distribution as no value at all. This is not how the data is
+ * actually stored; event metrics do in fact have an empty distribution after they are reset. But
+ * it's difficult to write assertions about expected metric data when any number of empty
+ * distributions can also be present, so they are screened out for convenience.
+ */
+public final class EventMetricSubject
+ extends AbstractMetricSubject {
+
+ /** {@link SubjectFactory} for assertions about {@link EventMetric} objects. */
+ private static final SubjectFactory
+ SUBJECT_FACTORY =
+ new SubjectFactory() {
+ // The Truth extensibility documentation indicates that the target should be nullable.
+ @Override
+ public EventMetricSubject getSubject(
+ FailureStrategy failureStrategy, @Nullable EventMetric target) {
+ return new EventMetricSubject(failureStrategy, target);
+ }
+ };
+
+ /** Static assertThat({@link EventMetric}) shortcut method. */
+ public static EventMetricSubject assertThat(@Nullable EventMetric metric) {
+ return assertAbout(SUBJECT_FACTORY).that(metric);
+ }
+
+ private EventMetricSubject(FailureStrategy strategy, EventMetric actual) {
+ super(strategy, actual);
+ }
+
+ /**
+ * Returns an indication to {@link AbstractMetricSubject#hasNoOtherValues} on whether a {@link
+ * MetricPoint} has a non-empty distribution.
+ */
+ @Override
+ protected boolean hasDefaultValue(MetricPoint metricPoint) {
+ return metricPoint.value().count() == 0;
+ }
+
+ /** Returns an appropriate string representation of a metric value for use in error messages. */
+ @Override
+ protected String getMessageRepresentation(Distribution distribution) {
+ StringBuilder sb = new StringBuilder("{");
+ boolean first = true;
+ for (Map.Entry, Long> entry :
+ distribution.intervalCounts().asMapOfRanges().entrySet()) {
+ if (entry.getValue() != 0L) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(',');
+ }
+ if (entry.getKey().hasLowerBound()) {
+ sb.append((entry.getKey().lowerBoundType() == BoundType.CLOSED) ? '[' : '(');
+ sb.append(entry.getKey().lowerEndpoint());
+ }
+ sb.append("..");
+ if (entry.getKey().hasUpperBound()) {
+ sb.append(entry.getKey().upperEndpoint());
+ sb.append((entry.getKey().upperBoundType() == BoundType.CLOSED) ? ']' : ')');
+ }
+ sb.append('=');
+ sb.append(entry.getValue());
+ }
+ }
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/java/google/registry/monitoring/metrics/contrib/IncrementableMetricSubject.java b/java/google/registry/monitoring/metrics/contrib/IncrementableMetricSubject.java
new file mode 100644
index 000000000..aed68d27d
--- /dev/null
+++ b/java/google/registry/monitoring/metrics/contrib/IncrementableMetricSubject.java
@@ -0,0 +1,91 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// 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 google.registry.monitoring.metrics.contrib;
+
+import static com.google.common.truth.Truth.assertAbout;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.SubjectFactory;
+import google.registry.monitoring.metrics.IncrementableMetric;
+import google.registry.monitoring.metrics.MetricPoint;
+import javax.annotation.Nullable;
+
+/**
+ * Truth subject for the {@link IncrementableMetric} class.
+ *
+ * For use with the Google Truth framework. Usage:
+ *
+ *
assertThat(myIncrementableMetric)
+ * .hasValueForLabels(5, "label1", "label2", "label3")
+ * .and()
+ * .hasAnyValueForLabels("label1", "label2", "label4")
+ * .and()
+ * .hasNoOtherValues();
+ * assertThat(myIncrementableMetric)
+ * .doesNotHaveAnyValueForLabels("label1", "label2");
+ *
+ *
+ * The assertions treat a value of 0 as no value at all. This is not how the data is actually
+ * stored; zero is a valid value for incrementable metrics, and they do in fact have a value of zero
+ * after they are reset. But it's difficult to write assertions about expected metric data when any
+ * number of zero values can also be present, so they are screened out for convenience.
+ */
+public final class IncrementableMetricSubject
+ extends AbstractMetricSubject {
+
+ /** {@link SubjectFactory} for assertions about {@link IncrementableMetric} objects. */
+ private static final SubjectFactory
+ SUBJECT_FACTORY =
+ new SubjectFactory() {
+ // The Truth extensibility documentation indicates that the target should be nullable.
+ @Override
+ public IncrementableMetricSubject getSubject(
+ FailureStrategy failureStrategy, @Nullable IncrementableMetric target) {
+ return new IncrementableMetricSubject(failureStrategy, target);
+ }
+ };
+
+ /** Static assertThat({@link IncrementableMetric}) shortcut method. */
+ public static IncrementableMetricSubject assertThat(@Nullable IncrementableMetric metric) {
+ return assertAbout(SUBJECT_FACTORY).that(metric);
+ }
+
+ private IncrementableMetricSubject(FailureStrategy strategy, IncrementableMetric actual) {
+ super(strategy, actual);
+ }
+
+ /**
+ * Asserts that the metric has a given value for the specified label values. This is a convenience
+ * method that takes a long instead of a Long, for ease of use.
+ */
+ public And hasValueForLabels(long value, String... labels) {
+ return hasValueForLabels(Long.valueOf(value), labels);
+ }
+
+ /**
+ * Returns an indication to {@link AbstractMetricSubject#hasNoOtherValues} on whether a {@link
+ * MetricPoint} has a non-zero value.
+ */
+ @Override
+ protected boolean hasDefaultValue(MetricPoint metricPoint) {
+ return metricPoint.value() == 0L;
+ }
+
+ /** Returns an appropriate string representation of a metric value for use in error messages. */
+ @Override
+ protected String getMessageRepresentation(Long value) {
+ return String.valueOf(value);
+ }
+}
diff --git a/javatests/google/registry/monitoring/metrics/contrib/BUILD b/javatests/google/registry/monitoring/metrics/contrib/BUILD
new file mode 100644
index 000000000..af9bbb1c0
--- /dev/null
+++ b/javatests/google/registry/monitoring/metrics/contrib/BUILD
@@ -0,0 +1,28 @@
+package(
+ default_testonly = 1,
+ default_visibility = ["//java/google/registry:registry_project"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
+
+java_library(
+ name = "contrib",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/google/registry/monitoring/metrics",
+ "//java/google/registry/monitoring/metrics/contrib",
+ "@com_google_guava",
+ "@com_google_truth",
+ "@junit",
+ ],
+)
+
+GenTestRules(
+ name = "GeneratedTestRules",
+ test_files = glob(["*Test.java"]),
+ deps = [
+ ":contrib",
+ ],
+)
diff --git a/javatests/google/registry/monitoring/metrics/contrib/EventMetricSubjectTest.java b/javatests/google/registry/monitoring/metrics/contrib/EventMetricSubjectTest.java
new file mode 100644
index 000000000..4460c5b61
--- /dev/null
+++ b/javatests/google/registry/monitoring/metrics/contrib/EventMetricSubjectTest.java
@@ -0,0 +1,120 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// 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 google.registry.monitoring.metrics.contrib;
+
+import static com.google.common.truth.Truth.assertThat;
+import static google.registry.monitoring.metrics.contrib.EventMetricSubject.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import google.registry.monitoring.metrics.EventMetric;
+import google.registry.monitoring.metrics.LabelDescriptor;
+import google.registry.monitoring.metrics.MetricRegistryImpl;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class EventMetricSubjectTest {
+
+ private static final ImmutableSet LABEL_DESCRIPTORS =
+ ImmutableSet.of(
+ LabelDescriptor.create("species", "Sheep Species"),
+ LabelDescriptor.create("color", "Sheep Color"));
+
+ private static final EventMetric metric =
+ MetricRegistryImpl.getDefault()
+ .newEventMetric(
+ "/test/event/sheep",
+ "Sheep Latency",
+ "sheeplatency",
+ LABEL_DESCRIPTORS,
+ EventMetric.DEFAULT_FITTER);
+
+ @Before
+ public void before() {
+ metric.reset();
+ metric.record(2.5, "Domestic", "Green");
+ metric.record(10, "Bighorn", "Blue");
+ }
+
+ @Test
+ public void testWrongNumberOfLabels_fails() {
+ try {
+ assertThat(metric).hasAnyValueForLabels("Domestic");
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has a value for labels ."
+ + " It has labeled values <[Bighorn:Blue =>"
+ + " {[4.0..16.0)=1}, Domestic:Green => {[1.0..4.0)=1}]>");
+ }
+ }
+
+ @Test
+ public void testDoesNotHaveWrongNumberOfLabels_succeeds() {
+ assertThat(metric).doesNotHaveAnyValueForLabels("Domestic");
+ }
+
+ @Test
+ public void testHasAnyValueForLabels_success() {
+ assertThat(metric)
+ .hasAnyValueForLabels("Domestic", "Green")
+ .and()
+ .hasAnyValueForLabels("Bighorn", "Blue")
+ .and()
+ .hasNoOtherValues();
+ }
+
+ @Test
+ public void testDoesNotHaveValueForLabels_success() {
+ assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Blue");
+ }
+
+ @Test
+ public void testDoesNotHaveValueForLabels_failure() {
+ try {
+ assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Green");
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has no value for labels ."
+ + " It has a value of <{[1.0..4.0)=1}>");
+ }
+ }
+
+ @Test
+ public void testUnexpectedValue_failure() {
+ try {
+ assertThat(metric)
+ .hasAnyValueForLabels("Domestic", "Green")
+ .and()
+ .hasNoOtherValues();
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has ."
+ + " It has labeled values <[Bighorn:Blue =>"
+ + " {[4.0..16.0)=1}, Domestic:Green => {[1.0..4.0)=1}]>");
+ }
+ }
+}
diff --git a/javatests/google/registry/monitoring/metrics/contrib/IncrementableMetricSubjectTest.java b/javatests/google/registry/monitoring/metrics/contrib/IncrementableMetricSubjectTest.java
new file mode 100644
index 000000000..1098575a1
--- /dev/null
+++ b/javatests/google/registry/monitoring/metrics/contrib/IncrementableMetricSubjectTest.java
@@ -0,0 +1,138 @@
+// Copyright 2017 The Nomulus Authors. All Rights Reserved.
+//
+// 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 google.registry.monitoring.metrics.contrib;
+
+import static com.google.common.truth.Truth.assertThat;
+import static google.registry.monitoring.metrics.contrib.IncrementableMetricSubject.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.collect.ImmutableSet;
+import google.registry.monitoring.metrics.IncrementableMetric;
+import google.registry.monitoring.metrics.LabelDescriptor;
+import google.registry.monitoring.metrics.MetricRegistryImpl;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class IncrementableMetricSubjectTest {
+
+ private static final ImmutableSet LABEL_DESCRIPTORS =
+ ImmutableSet.of(
+ LabelDescriptor.create("species", "Sheep Species"),
+ LabelDescriptor.create("color", "Sheep Color"));
+
+ private static final IncrementableMetric metric =
+ MetricRegistryImpl.getDefault()
+ .newIncrementableMetric(
+ "/test/incrementable/sheep",
+ "Count of Sheep",
+ "sheepcount",
+ LABEL_DESCRIPTORS);
+
+ @Before
+ public void before() {
+ metric.reset();
+ metric.increment("Domestic", "Green");
+ metric.incrementBy(2, "Bighorn", "Blue");
+ }
+
+ @Test
+ public void testWrongNumberOfLabels_fails() {
+ try {
+ assertThat(metric).hasValueForLabels(1, "Domestic");
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has a value for labels ."
+ + " It has labeled values <[Bighorn:Blue => 2, Domestic:Green => 1]>");
+ }
+ }
+
+ @Test
+ public void testDoesNotHaveWrongNumberOfLabels_succeeds() {
+ assertThat(metric).doesNotHaveAnyValueForLabels("Domestic");
+ }
+
+ @Test
+ public void testHasValueForLabels_success() {
+ assertThat(metric)
+ .hasValueForLabels(1, "Domestic", "Green")
+ .and()
+ .hasValueForLabels(2, "Bighorn", "Blue")
+ .and()
+ .hasNoOtherValues();
+ }
+
+ @Test
+ public void testHasAnyValueForLabels_success() {
+ assertThat(metric)
+ .hasAnyValueForLabels("Domestic", "Green")
+ .and()
+ .hasAnyValueForLabels("Bighorn", "Blue")
+ .and()
+ .hasNoOtherValues();
+ }
+
+ @Test
+ public void testDoesNotHaveValueForLabels_success() {
+ assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Blue");
+ }
+
+ @Test
+ public void testDoesNotHaveValueForLabels_failure() {
+ try {
+ assertThat(metric).doesNotHaveAnyValueForLabels("Domestic", "Green");
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has no value for labels ."
+ + " It has a value of <1>");
+ }
+ }
+
+ @Test
+ public void testWrongValue_failure() {
+ try {
+ assertThat(metric).hasValueForLabels(2, "Domestic", "Green");
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has a value of 2"
+ + " for labels . It has a value of <1>");
+ }
+ }
+
+ @Test
+ public void testUnexpectedValue_failure() {
+ try {
+ assertThat(metric).hasValueForLabels(1, "Domestic", "Green").and().hasNoOtherValues();
+ fail("Expected assertion error");
+ } catch (AssertionError e) {
+ assertThat(e)
+ .hasMessageThat()
+ .isEqualTo(
+ "Not true that has ."
+ + " It has labeled values <[Bighorn:Blue => 2, Domestic:Green => 1]>");
+ }
+ }
+}