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

prototest: fix early return condition in AssertElementsMatch #17416

Merged
merged 9 commits into from
May 22, 2023
23 changes: 16 additions & 7 deletions proto/private/prototest/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,21 @@ func AssertDeepEqual(t TestingT, x, y interface{}, opts ...cmp.Option) {
func AssertElementsMatch[V any](
t TestingT, listX, listY []V, opts ...cmp.Option,
) {
t.Helper()
diff := diffElements(listX, listY)
if diff != "" {
t.Fatalf("assertion failed: slices do not have matching elements\n--- expected\n+++ actual\n%v", diff)
}
}

func diffElements[V any](
listX, listY []V, opts ...cmp.Option,
) string {
if len(listX) == 0 && len(listY) == 0 {
return
return ""
}

if len(listX) != len(listY) {
return cmp.Diff(listX, listY, opts...)
}

opts = append(opts, protocmp.Transform())
Expand Down Expand Up @@ -63,8 +74,8 @@ func AssertElementsMatch[V any](
}
}

if len(outX) == len(outY) && len(listX) == len(listY) {
return // matches
if len(outX) == len(listX) && len(outY) == len(listY) {
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the bug.

return "" // matches
}

// dump remainder into the slice so we can generate a useful error
Expand All @@ -75,9 +86,7 @@ func AssertElementsMatch[V any](
outY = append(outY, itemY)
}

if diff := cmp.Diff(outX, outY, opts...); diff != "" {
t.Fatalf("assertion failed: slices do not have matching elements\n--- expected\n+++ actual\n%v", diff)
}
return cmp.Diff(outX, outY, opts...)
}

func AssertContainsElement[V any](t TestingT, list []V, element V, opts ...cmp.Option) {
Expand Down
103 changes: 103 additions & 0 deletions proto/private/prototest/testing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package prototest

import (
"strconv"
"testing"

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

type wrap struct {
V int
O string
}

func (w *wrap) String() string {
return strconv.Itoa(w.V)
}

func (w *wrap) GoString() string {
return w.String()
}

func TestDiffElements_noProtobufs(t *testing.T) {
// NOTE: this test only tests non-protobuf slices initially

type testcase struct {
a, b []*wrap
notSame bool
}

run := func(t *testing.T, tc testcase) {
diff := diffElements(tc.a, tc.b)
if tc.notSame {
require.False(t, diff == "", "expected not to be the same")
} else {
require.True(t, diff == "", "expected to be the same")
}
}

w := func(v int) *wrap {
return &wrap{V: v}
}

cases := map[string]testcase{
"nil": {},
"empty": {a: []*wrap{}, b: []*wrap{}},
"nil and empty": {a: []*wrap{}, b: nil},
"ordered match": {
a: []*wrap{w(1), w(22), w(303), w(43004), w(-5)},
b: []*wrap{w(1), w(22), w(303), w(43004), w(-5)},
},
"permuted match": {
a: []*wrap{w(1), w(22), w(303), w(43004), w(-5)},
b: []*wrap{w(-5), w(43004), w(303), w(22), w(1)},
},
"duplicates": {
a: []*wrap{w(1), w(2), w(2), w(3)},
b: []*wrap{w(2), w(1), w(3), w(2)},
},
// no match
"1 vs nil": {
a: []*wrap{w(1)},
b: nil,
notSame: true,
},
"1 vs 2": {
a: []*wrap{w(1)},
b: []*wrap{w(2)},
notSame: true,
},
"1,2 vs 2,3": {
a: []*wrap{w(1), w(2)},
b: []*wrap{w(2), w(3)},
notSame: true,
},
"1,2 vs 1,2,3": {
a: []*wrap{w(1), w(2)},
b: []*wrap{w(1), w(2), w(3)},
notSame: true,
},
"duplicates omitted": {
a: []*wrap{w(1), w(2), w(2), w(3)},
b: []*wrap{w(1), w(3), w(2)},
notSame: true,
},
}

allCases := make(map[string]testcase)
for name, tc := range cases {
allCases[name] = tc
allCases[name+" (flipped)"] = testcase{
a: tc.b,
b: tc.a,
notSame: tc.notSame,
}
}

for name, tc := range allCases {
t.Run(name, func(t *testing.T) {
run(t, tc)
})
}
}