diff --git a/prometheus/testutil/testutil.go b/prometheus/testutil/testutil.go index e7af303e6..973b82d81 100644 --- a/prometheus/testutil/testutil.go +++ b/prometheus/testutil/testutil.go @@ -138,20 +138,13 @@ func GatherAndCompare(g prometheus.Gatherer, expected io.Reader, metricNames ... } want := internal.NormalizeMetricFamilies(wantRaw) - if len(got) != len(want) { - return notMatchingError(got, want) - } - for i := range got { - if got[i].String() != want[i].String() { - return notMatchingError(got, want) - } - } - return nil + return compare(got, want) } -// notMatchingError encodes both provided slices of metric families into the -// text format and creates a readable error message from the result. -func notMatchingError(got, want []*dto.MetricFamily) error { +// compare encodes both provided slices of metric families into the text format, +// compares their string message, and returns an error if they do not match. +// The error contains the encoded text, of both the desired, and actual result. +func compare(got, want []*dto.MetricFamily) error { var gotBuf, wantBuf bytes.Buffer enc := expfmt.NewEncoder(&gotBuf, expfmt.FmtText) for _, mf := range got { @@ -166,7 +159,8 @@ func notMatchingError(got, want []*dto.MetricFamily) error { } } - return fmt.Errorf(` + if wantBuf.String() != gotBuf.String() { + return fmt.Errorf(` metric output does not match expectation; want: %s @@ -175,6 +169,9 @@ got: %s `, wantBuf.String(), gotBuf.String()) + + } + return nil } func filterMetrics(metrics []*dto.MetricFamily, names []string) []*dto.MetricFamily { diff --git a/prometheus/testutil/testutil_test.go b/prometheus/testutil/testutil_test.go index 0b8cf0934..f9600779e 100644 --- a/prometheus/testutil/testutil_test.go +++ b/prometheus/testutil/testutil_test.go @@ -165,6 +165,82 @@ func TestCollectAndCompareNoLabel(t *testing.T) { } } +func TestCollectAndCompareHistogram(t *testing.T) { + inputs := []struct { + name string + c prometheus.Collector + metadata string + expect string + labels []string + observation float64 + }{ + { + name: "Testing Histogram Collector", + c: prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "some_histogram", + Help: "An example of a histogram", + Buckets: []float64{1, 2, 3}, + }), + metadata: ` + # HELP some_histogram An example of a histogram + # TYPE some_histogram histogram + `, + expect: ` + some_histogram{le="1"} 0 + some_histogram{le="2"} 0 + some_histogram{le="3"} 1 + some_histogram_bucket{le="+Inf"} 1 + some_histogram_sum 2.5 + some_histogram_count 1 + + `, + observation: 2.5, + }, + { + name: "Testing HistogramVec Collector", + c: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "some_histogram", + Help: "An example of a histogram", + Buckets: []float64{1, 2, 3}, + }, []string{"test"}), + + metadata: ` + # HELP some_histogram An example of a histogram + # TYPE some_histogram histogram + `, + expect: ` + some_histogram_bucket{test="test",le="1"} 0 + some_histogram_bucket{test="test",le="2"} 0 + some_histogram_bucket{test="test",le="3"} 1 + some_histogram_bucket{test="test",le="+Inf"} 1 + some_histogram_sum{test="test"} 2.5 + some_histogram_count{test="test"} 1 + + `, + observation: 2.5, + }, + } + + for _, input := range inputs { + switch collector := input.c.(type) { + case prometheus.Histogram: + collector.Observe(input.observation) + case *prometheus.HistogramVec: + collector.WithLabelValues("test").Observe(input.observation) + default: + t.Fatalf("unsuported collector tested") + + } + + t.Run(input.name, func(t *testing.T) { + if err := CollectAndCompare(input.c, strings.NewReader(input.metadata+input.expect)); err != nil { + t.Errorf("unexpected collecting result:\n%s", err) + } + }) + + } +} + func TestNoMetricFilter(t *testing.T) { const metadata = ` # HELP some_total A value that represents a counter.