Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

smart diff to testutil.GatherAndCompare #998

Merged
merged 9 commits into from Apr 13, 2022
1 change: 1 addition & 0 deletions go.mod
Expand Up @@ -8,6 +8,7 @@ require (
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.32.1
github.com/prometheus/procfs v0.7.3
github.com/stretchr/testify v1.4.0
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
google.golang.org/protobuf v1.26.0
)
Expand Down
70 changes: 69 additions & 1 deletion prometheus/testutil/testutil.go
Expand Up @@ -41,8 +41,10 @@ import (
"bytes"
"fmt"
"io"
"testing"

"github.com/prometheus/common/expfmt"
"github.com/stretchr/testify/require"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm.. is there a way to avoid this package? For a reason we wanted to make sure it's not used, so we are not leaking it.

Since this is not testing code, every project that imports client_golang needs to import testify.... I think we want to copy the diff function and maintain here.

See how we did that here https://github.com/efficientgo/tools/blob/main/core/pkg/testutil/testutil.go#L101

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally agree. I think I was a little hasty to review this, and I haven't noticed testify is not there already 🙈

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess because it's already a transitive dependency.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I agree too, this makes sense
but the above diff function has a dep on
https://github.com/efficientgo/tools/blob/fe763185946be83b20da626605319733bd7f97cb/core/pkg/testutil/testutil.go#L16 which is not maintained should I transfer the module implementation over here or just simply import it.


dto "github.com/prometheus/client_model/go"

Expand Down Expand Up @@ -215,13 +217,79 @@ func compare(got, want []*dto.MetricFamily) error {
if wantBuf.String() != gotBuf.String() {
return fmt.Errorf(`
metric output does not match expectation; want:
%s
got:
%s`, wantBuf.String(), gotBuf.String())

}
return nil
}

// CollectAndCompareV2 is similar to CollectAndCompare except it takes *testing.T object
// and shouldFail Flag to pass down to GatherAndCompareV2.
func CollectAndCompareV2(t *testing.T, c prometheus.Collector, expected io.Reader, shouldFail bool, metricNames ...string) error {
reg := prometheus.NewPedanticRegistry()
if err := reg.Register(c); err != nil {
return fmt.Errorf("registering collector failed: %s", err)
}
return GatherAndCompareV2(t, reg, expected, shouldFail, metricNames...)
}

// GatherAndCompareV2 is similiar to GatherAndCompare except it takes t *testing.T and shouldFail flag
// and calls TransactionalGatherAndCompareV2.
func GatherAndCompareV2(t *testing.T, g prometheus.Gatherer, expected io.Reader, shouldFail bool, metricNames ...string) error {
return TransactionalGatherAndCompareV2(t, prometheus.ToTransactionalGatherer(g), expected, shouldFail, metricNames...)
}

// TransactionalGatherAndCompareV2 is similiar to TransactionalGatherAndCompare except
// it takes t *testing.T and shouldFail flag and calls compareV2 for better diff.
func TransactionalGatherAndCompareV2(t *testing.T, g prometheus.TransactionalGatherer, expected io.Reader, shouldFail bool, metricNames ...string) error {
got, done, err := g.Gather()
defer done()
if err != nil {
return fmt.Errorf("gathering metrics failed: %s", err)
}
if metricNames != nil {
got = filterMetrics(got, metricNames)
}
var tp expfmt.TextParser
wantRaw, err := tp.TextToMetricFamilies(expected)
if err != nil {
return fmt.Errorf("parsing expected metrics failed: %s", err)
}
want := internal.NormalizeMetricFamilies(wantRaw)

return compareV2(t, got, want, shouldFail)
}

// compareV2 accepts *testing.T object and uses require package to provide
// a better diff between got and want.
func compareV2(t *testing.T, got, want []*dto.MetricFamily, shouldFail bool) error {
var gotBuf, wantBuf bytes.Buffer
enc := expfmt.NewEncoder(&gotBuf, expfmt.FmtText)
for _, mf := range got {
if err := enc.Encode(mf); err != nil {
return fmt.Errorf("encoding gathered metrics failed: %s", err)
}
}
enc = expfmt.NewEncoder(&wantBuf, expfmt.FmtText)
for _, mf := range want {
if err := enc.Encode(mf); err != nil {
return fmt.Errorf("encoding expected metrics failed: %s", err)
}
}

if shouldFail && wantBuf.String() != gotBuf.String() {
return fmt.Errorf(`
metric output does not match expectation; want:

%s
got:

%s`, wantBuf.String(), gotBuf.String())

}

require.Equal(t, wantBuf.String(), gotBuf.String())
return nil
}

Expand Down
15 changes: 7 additions & 8 deletions prometheus/testutil/testutil_test.go
Expand Up @@ -18,6 +18,7 @@ import (
"testing"

"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)

type untypedCollector struct{}
Expand Down Expand Up @@ -138,7 +139,7 @@ func TestCollectAndCompare(t *testing.T) {
some_total{ label1 = "value1" } 1
`

if err := CollectAndCompare(c, strings.NewReader(metadata+expected), "some_total"); err != nil {
if err := CollectAndCompareV2(t, c, strings.NewReader(metadata+expected), false, "some_total"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
Expand All @@ -160,7 +161,7 @@ func TestCollectAndCompareNoLabel(t *testing.T) {
some_total 1
`

if err := CollectAndCompare(c, strings.NewReader(metadata+expected), "some_total"); err != nil {
if err := CollectAndCompareV2(t, c, strings.NewReader(metadata+expected), false, "some_total"); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
Expand Down Expand Up @@ -232,7 +233,7 @@ func TestCollectAndCompareHistogram(t *testing.T) {
}

t.Run(input.name, func(t *testing.T) {
if err := CollectAndCompare(input.c, strings.NewReader(input.metadata+input.expect)); err != nil {
if err := CollectAndCompareV2(t, input.c, strings.NewReader(input.metadata+input.expect), false); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
})
Expand All @@ -259,7 +260,7 @@ func TestNoMetricFilter(t *testing.T) {
some_total{label1="value1"} 1
`

if err := CollectAndCompare(c, strings.NewReader(metadata+expected)); err != nil {
if err := CollectAndCompareV2(t, c, strings.NewReader(metadata+expected), false); err != nil {
t.Errorf("unexpected collecting result:\n%s", err)
}
}
Expand Down Expand Up @@ -297,14 +298,12 @@ got:
some_total{label1="value1"} 1
`

err := CollectAndCompare(c, strings.NewReader(metadata+expected))
err := CollectAndCompareV2(t, c, strings.NewReader(metadata+expected), true)
if err == nil {
t.Error("Expected error, got no error.")
}

if err.Error() != expectedError {
t.Errorf("Expected\n%#+v\nGot:\n%#+v", expectedError, err.Error())
}
require.EqualErrorf(t, err, expectedError, "Expected\n%#+v\nGot:\n%#+v", expectedError, err.Error())
}

func TestCollectAndCount(t *testing.T) {
Expand Down