google / google/go-cmp

Diff may omit difference of map in struct in slice

Open
#329 7 comments 1 reaction 0 assignees View on GitHub
reporter
Dominant language
Go
Stars
4.7k
Forks
243
PR merge metrics
No merged PRs in 30d

Description

I've run into a case where the relevant difference is omitted from the cmp.Diff output. I have differences within a large (≥18 entries) map that is nested inside a slice of structs, with some other fields preceding it:

```Go
func TestMapDiff(t *testing.T) {
const (
// Diff only outputs the first 8 map entries, so if they're identical, the diff is useless
numSame = 8
// only 10 differing entries or more trigger the issue
numDiff = 10
)
type Wrapper struct {
// we need a couple of unrelated fields above the map to trigger the issue,
// slices or other maps seem to work well (strings don't)
UnrelatedField1 []string
UnrelatedField2 []string
UnrelatedField3 []string
UnrelatedField4 []string
UnrelatedField5 []string
UnrelatedField6 []string
// this is where the difference is
DifferingMap map[string]any
}
want := map[string]any{}
got := map[string]any{}
for i := 0; i < numSame+numDiff; i++ {
key := fmt.Sprintf("key-%02d", i)
if i < numSame {
want[key] = "same"
got[key] = "same"
} else {
// note that the missing entries must come after the present ones, alphabetically
want[key] = "missing"
}
}
if diff := cmp.Diff(
// we must use a slice to trigger the issue, comparing plain Wrappers doesn't trigger it
[]*Wrapper{
{
// To reproduce the issue, these must not be nil
UnrelatedField1: []string{},
UnrelatedField2: []string{},
UnrelatedField3: []string{},
UnrelatedField4: []string{},
UnrelatedField5: []string{},
UnrelatedField6: []string{},
DifferingMap: want,
},
},
[]*Wrapper{
{
UnrelatedField1: []string{},
UnrelatedField2: []string{},
UnrelatedField3: []string{},
UnrelatedField4: []string{},
UnrelatedField5: []string{},
UnrelatedField6: []string{},
DifferingMap: got,
},
}); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
}
```
This leads to the following output:
```
[]*main.Wrapper{
- &{
- UnrelatedField1: []string{},
- UnrelatedField2: []string{},
- UnrelatedField3: []string{},
- UnrelatedField4: []string{},
- UnrelatedField5: []string{},
- UnrelatedField6: []string{},
- DifferingMap: map[string]any{
- "key-00": string("same"),
- "key-01": string("same"),
- "key-02": string("same"),
- "key-03": string("same"),
- "key-04": string("same"),
- "key-05": string("same"),
- "key-06": string("same"),
- "key-07": string("same"),
- ...
- },
- },
+ &{
+ UnrelatedField1: []string{},
+ UnrelatedField2: []string{},
+ UnrelatedField3: []string{},
+ UnrelatedField4: []string{},
+ UnrelatedField5: []string{},
+ UnrelatedField6: []string{},
+ DifferingMap: map[string]any{
+ "key-00": string("same"),
+ "key-01": string("same"),
+ "key-02": string("same"),
+ "key-03": string("same"),
+ "key-04": string("same"),
+ "key-05": string("same"),
+ "key-06": string("same"),
+ "key-07": string("same"),
+ },
+ },
}
```
Note how the whole Wrapper is marked as differing, instead of just the map inside, and the interesting differing part of the map is omitted, likely as a direct result.

Reducing the number of differences inside the nested map to 9 yields this much more useful output:
```
  []*main.Wrapper{
   &{
   ... // 4 identical fields
   UnrelatedField5: {},
   UnrelatedField6: {},
   DifferingMap: map[string]any{
   ... // 6 identical entries
   "key-06": string("same"),
   "key-07": string("same"),
-  "key-08": string("missing"),
-  "key-09": string("missing"),
-  "key-10": string("missing"),
-  "key-11": string("missing"),
-  "key-12": string("missing"),
-  "key-13": string("missing"),
-  "key-14": string("missing"),
-  "key-15": string("missing"),
-  "key-16": string("missing"),
   },
   },
  }
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.