mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2020-11-18 19:53:40 -08:00
2830 lines
80 KiB
Go
2830 lines
80 KiB
Go
// Copyright 2017, The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE.md file.
|
|
|
|
package cmp_test
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/md5"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"math/rand"
|
|
"reflect"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/google/go-cmp/cmp/internal/flags"
|
|
|
|
pb "github.com/google/go-cmp/cmp/internal/testprotos"
|
|
ts "github.com/google/go-cmp/cmp/internal/teststructs"
|
|
)
|
|
|
|
func init() {
|
|
flags.Deterministic = true
|
|
}
|
|
|
|
var now = time.Date(2009, time.November, 10, 23, 00, 00, 00, time.UTC)
|
|
|
|
func intPtr(n int) *int { return &n }
|
|
|
|
type test struct {
|
|
label string // Test name
|
|
x, y interface{} // Input values to compare
|
|
opts []cmp.Option // Input options
|
|
wantDiff string // The exact difference string
|
|
wantPanic string // Sub-string of an expected panic message
|
|
reason string // The reason for the expected outcome
|
|
}
|
|
|
|
func TestDiff(t *testing.T) {
|
|
var tests []test
|
|
tests = append(tests, comparerTests()...)
|
|
tests = append(tests, transformerTests()...)
|
|
tests = append(tests, embeddedTests()...)
|
|
tests = append(tests, methodTests()...)
|
|
tests = append(tests, project1Tests()...)
|
|
tests = append(tests, project2Tests()...)
|
|
tests = append(tests, project3Tests()...)
|
|
tests = append(tests, project4Tests()...)
|
|
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.label, func(t *testing.T) {
|
|
t.Parallel()
|
|
var gotDiff, gotPanic string
|
|
func() {
|
|
defer func() {
|
|
if ex := recover(); ex != nil {
|
|
if s, ok := ex.(string); ok {
|
|
gotPanic = s
|
|
} else {
|
|
panic(ex)
|
|
}
|
|
}
|
|
}()
|
|
gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...)
|
|
}()
|
|
// TODO: Require every test case to provide a reason.
|
|
if tt.wantPanic == "" {
|
|
if gotPanic != "" {
|
|
t.Fatalf("unexpected panic message: %s\nreason: %v", gotPanic, tt.reason)
|
|
}
|
|
tt.wantDiff = strings.TrimPrefix(tt.wantDiff, "\n")
|
|
if gotDiff != tt.wantDiff {
|
|
t.Fatalf("difference message:\ngot:\n%s\nwant:\n%s\nreason: %v", gotDiff, tt.wantDiff, tt.reason)
|
|
}
|
|
} else {
|
|
if !strings.Contains(gotPanic, tt.wantPanic) {
|
|
t.Fatalf("panic message:\ngot: %s\nwant: %s\nreason: %v", gotPanic, tt.wantPanic, tt.reason)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func comparerTests() []test {
|
|
const label = "Comparer"
|
|
|
|
type Iface1 interface {
|
|
Method()
|
|
}
|
|
type Iface2 interface {
|
|
Method()
|
|
}
|
|
|
|
type tarHeader struct {
|
|
Name string
|
|
Mode int64
|
|
Uid int
|
|
Gid int
|
|
Size int64
|
|
ModTime time.Time
|
|
Typeflag byte
|
|
Linkname string
|
|
Uname string
|
|
Gname string
|
|
Devmajor int64
|
|
Devminor int64
|
|
AccessTime time.Time
|
|
ChangeTime time.Time
|
|
Xattrs map[string]string
|
|
}
|
|
|
|
makeTarHeaders := func(tf byte) (hs []tarHeader) {
|
|
for i := 0; i < 5; i++ {
|
|
hs = append(hs, tarHeader{
|
|
Name: fmt.Sprintf("some/dummy/test/file%d", i),
|
|
Mode: 0664, Uid: i * 1000, Gid: i * 1000, Size: 1 << uint(i),
|
|
ModTime: now.Add(time.Duration(i) * time.Hour),
|
|
Uname: "user", Gname: "group",
|
|
Typeflag: tf,
|
|
})
|
|
}
|
|
return hs
|
|
}
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: nil,
|
|
y: nil,
|
|
}, {
|
|
label: label,
|
|
x: 1,
|
|
y: 1,
|
|
}, {
|
|
label: label,
|
|
x: 1,
|
|
y: 1,
|
|
opts: []cmp.Option{cmp.Ignore()},
|
|
wantPanic: "cannot use an unfiltered option",
|
|
}, {
|
|
label: label,
|
|
x: 1,
|
|
y: 1,
|
|
opts: []cmp.Option{cmp.Comparer(func(_, _ interface{}) bool { return true })},
|
|
wantPanic: "cannot use an unfiltered option",
|
|
}, {
|
|
label: label,
|
|
x: 1,
|
|
y: 1,
|
|
opts: []cmp.Option{cmp.Transformer("λ", func(x interface{}) interface{} { return x })},
|
|
wantPanic: "cannot use an unfiltered option",
|
|
}, {
|
|
label: label,
|
|
x: 1,
|
|
y: 1,
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y int) bool { return true }),
|
|
cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
|
|
},
|
|
wantPanic: "ambiguous set of applicable options",
|
|
}, {
|
|
label: label,
|
|
x: 1,
|
|
y: 1,
|
|
opts: []cmp.Option{
|
|
cmp.FilterPath(func(p cmp.Path) bool {
|
|
return len(p) > 0 && p[len(p)-1].Type().Kind() == reflect.Int
|
|
}, cmp.Options{cmp.Ignore(), cmp.Ignore(), cmp.Ignore()}),
|
|
cmp.Comparer(func(x, y int) bool { return true }),
|
|
cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
|
|
},
|
|
}, {
|
|
label: label,
|
|
opts: []cmp.Option{struct{ cmp.Option }{}},
|
|
wantPanic: "unknown option",
|
|
}, {
|
|
label: label,
|
|
x: struct{ A, B, C int }{1, 2, 3},
|
|
y: struct{ A, B, C int }{1, 2, 3},
|
|
}, {
|
|
label: label,
|
|
x: struct{ A, B, C int }{1, 2, 3},
|
|
y: struct{ A, B, C int }{1, 2, 4},
|
|
wantDiff: `
|
|
struct{ A int; B int; C int }{
|
|
A: 1,
|
|
B: 2,
|
|
- C: 3,
|
|
+ C: 4,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: struct{ a, b, c int }{1, 2, 3},
|
|
y: struct{ a, b, c int }{1, 2, 4},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: &struct{ A *int }{intPtr(4)},
|
|
y: &struct{ A *int }{intPtr(4)},
|
|
}, {
|
|
label: label,
|
|
x: &struct{ A *int }{intPtr(4)},
|
|
y: &struct{ A *int }{intPtr(5)},
|
|
wantDiff: `
|
|
&struct{ A *int }{
|
|
- A: &4,
|
|
+ A: &5,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: &struct{ A *int }{intPtr(4)},
|
|
y: &struct{ A *int }{intPtr(5)},
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y int) bool { return true }),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: &struct{ A *int }{intPtr(4)},
|
|
y: &struct{ A *int }{intPtr(5)},
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: &struct{ R *bytes.Buffer }{},
|
|
y: &struct{ R *bytes.Buffer }{},
|
|
}, {
|
|
label: label,
|
|
x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
|
|
y: &struct{ R *bytes.Buffer }{},
|
|
wantDiff: `
|
|
&struct{ R *bytes.Buffer }{
|
|
- R: s"",
|
|
+ R: nil,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
|
|
y: &struct{ R *bytes.Buffer }{},
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y io.Reader) bool { return true }),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: &struct{ R bytes.Buffer }{},
|
|
y: &struct{ R bytes.Buffer }{},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: &struct{ R bytes.Buffer }{},
|
|
y: &struct{ R bytes.Buffer }{},
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y io.Reader) bool { return true }),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: &struct{ R bytes.Buffer }{},
|
|
y: &struct{ R bytes.Buffer }{},
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }),
|
|
cmp.Comparer(func(x, y io.Reader) bool { return true }),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
|
|
y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
|
|
y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
|
|
opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool {
|
|
if x == nil || y == nil {
|
|
return x == nil && y == nil
|
|
}
|
|
return x.String() == y.String()
|
|
})},
|
|
}, {
|
|
label: label,
|
|
x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
|
|
y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*d*")},
|
|
opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool {
|
|
if x == nil || y == nil {
|
|
return x == nil && y == nil
|
|
}
|
|
return x.String() == y.String()
|
|
})},
|
|
wantDiff: `
|
|
[]*regexp.Regexp{
|
|
nil,
|
|
- s"a*b*c*",
|
|
+ s"a*b*d*",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: func() ***int {
|
|
a := 0
|
|
b := &a
|
|
c := &b
|
|
return &c
|
|
}(),
|
|
y: func() ***int {
|
|
a := 0
|
|
b := &a
|
|
c := &b
|
|
return &c
|
|
}(),
|
|
}, {
|
|
label: label,
|
|
x: func() ***int {
|
|
a := 0
|
|
b := &a
|
|
c := &b
|
|
return &c
|
|
}(),
|
|
y: func() ***int {
|
|
a := 1
|
|
b := &a
|
|
c := &b
|
|
return &c
|
|
}(),
|
|
wantDiff: `
|
|
&&&int(
|
|
- 0,
|
|
+ 1,
|
|
)
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: []int{1, 2, 3, 4, 5}[:3],
|
|
y: []int{1, 2, 3},
|
|
}, {
|
|
label: label,
|
|
x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")},
|
|
y: struct{ fmt.Stringer }{regexp.MustCompile("hello")},
|
|
opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
|
|
}, {
|
|
label: label,
|
|
x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")},
|
|
y: struct{ fmt.Stringer }{regexp.MustCompile("hello2")},
|
|
opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
|
|
wantDiff: `
|
|
struct{ fmt.Stringer }(
|
|
- s"hello",
|
|
+ s"hello2",
|
|
)
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: md5.Sum([]byte{'a'}),
|
|
y: md5.Sum([]byte{'b'}),
|
|
wantDiff: `
|
|
[16]uint8{
|
|
- 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61,
|
|
+ 0x92, 0xeb, 0x5f, 0xfe, 0xe6, 0xae, 0x2f, 0xec, 0x3a, 0xd7, 0x1c, 0x77, 0x75, 0x31, 0x57, 0x8f,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: new(fmt.Stringer),
|
|
y: nil,
|
|
wantDiff: `
|
|
interface{}(
|
|
- &fmt.Stringer(nil),
|
|
)
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: makeTarHeaders('0'),
|
|
y: makeTarHeaders('\x00'),
|
|
wantDiff: `
|
|
[]cmp_test.tarHeader{
|
|
{
|
|
... // 4 identical fields
|
|
Size: 1,
|
|
ModTime: s"2009-11-10 23:00:00 +0000 UTC",
|
|
- Typeflag: 0x30,
|
|
+ Typeflag: 0x00,
|
|
Linkname: "",
|
|
Uname: "user",
|
|
... // 6 identical fields
|
|
},
|
|
{
|
|
... // 4 identical fields
|
|
Size: 2,
|
|
ModTime: s"2009-11-11 00:00:00 +0000 UTC",
|
|
- Typeflag: 0x30,
|
|
+ Typeflag: 0x00,
|
|
Linkname: "",
|
|
Uname: "user",
|
|
... // 6 identical fields
|
|
},
|
|
{
|
|
... // 4 identical fields
|
|
Size: 4,
|
|
ModTime: s"2009-11-11 01:00:00 +0000 UTC",
|
|
- Typeflag: 0x30,
|
|
+ Typeflag: 0x00,
|
|
Linkname: "",
|
|
Uname: "user",
|
|
... // 6 identical fields
|
|
},
|
|
{
|
|
... // 4 identical fields
|
|
Size: 8,
|
|
ModTime: s"2009-11-11 02:00:00 +0000 UTC",
|
|
- Typeflag: 0x30,
|
|
+ Typeflag: 0x00,
|
|
Linkname: "",
|
|
Uname: "user",
|
|
... // 6 identical fields
|
|
},
|
|
{
|
|
... // 4 identical fields
|
|
Size: 16,
|
|
ModTime: s"2009-11-11 03:00:00 +0000 UTC",
|
|
- Typeflag: 0x30,
|
|
+ Typeflag: 0x00,
|
|
Linkname: "",
|
|
Uname: "user",
|
|
... // 6 identical fields
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: make([]int, 1000),
|
|
y: make([]int, 1000),
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(_, _ int) bool {
|
|
return rand.Intn(2) == 0
|
|
}),
|
|
},
|
|
wantPanic: "non-deterministic or non-symmetric function detected",
|
|
}, {
|
|
label: label,
|
|
x: make([]int, 1000),
|
|
y: make([]int, 1000),
|
|
opts: []cmp.Option{
|
|
cmp.FilterValues(func(_, _ int) bool {
|
|
return rand.Intn(2) == 0
|
|
}, cmp.Ignore()),
|
|
},
|
|
wantPanic: "non-deterministic or non-symmetric function detected",
|
|
}, {
|
|
label: label,
|
|
x: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
|
y: []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y int) bool {
|
|
return x < y
|
|
}),
|
|
},
|
|
wantPanic: "non-deterministic or non-symmetric function detected",
|
|
}, {
|
|
label: label,
|
|
x: make([]string, 1000),
|
|
y: make([]string, 1000),
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("λ", func(x string) int {
|
|
return rand.Int()
|
|
}),
|
|
},
|
|
wantPanic: "non-deterministic function detected",
|
|
}, {
|
|
// Make sure the dynamic checks don't raise a false positive for
|
|
// non-reflexive comparisons.
|
|
label: label,
|
|
x: make([]int, 10),
|
|
y: make([]int, 10),
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("λ", func(x int) float64 {
|
|
return math.NaN()
|
|
}),
|
|
},
|
|
wantDiff: `
|
|
[]int{
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
- Inverse(λ, float64(NaN)),
|
|
+ Inverse(λ, float64(NaN)),
|
|
}
|
|
`,
|
|
}, {
|
|
// Ensure reasonable Stringer formatting of map keys.
|
|
label: label,
|
|
x: map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}},
|
|
y: map[*pb.Stringer]*pb.Stringer(nil),
|
|
wantDiff: `
|
|
map[*testprotos.Stringer]*testprotos.Stringer(
|
|
- {⟪0xdeadf00f⟫: s"world"},
|
|
+ nil,
|
|
)
|
|
`,
|
|
}, {
|
|
// Ensure Stringer avoids double-quote escaping if possible.
|
|
label: label,
|
|
x: []*pb.Stringer{{`multi\nline\nline\nline`}},
|
|
wantDiff: strings.Replace(`
|
|
interface{}(
|
|
- []*testprotos.Stringer{s'multi\nline\nline\nline'},
|
|
)
|
|
`, "'", "`", -1),
|
|
}, {
|
|
label: label,
|
|
x: struct{ I Iface2 }{},
|
|
y: struct{ I Iface2 }{},
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y Iface1) bool {
|
|
return x == nil && y == nil
|
|
}),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: struct{ I Iface2 }{},
|
|
y: struct{ I Iface2 }{},
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("λ", func(v Iface1) bool {
|
|
return v == nil
|
|
}),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: struct{ I Iface2 }{},
|
|
y: struct{ I Iface2 }{},
|
|
opts: []cmp.Option{
|
|
cmp.FilterValues(func(x, y Iface1) bool {
|
|
return x == nil && y == nil
|
|
}, cmp.Ignore()),
|
|
},
|
|
}, {
|
|
label: label,
|
|
x: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}},
|
|
y: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65.0, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63.0, "name": "Sammy Sosa"}},
|
|
wantDiff: `
|
|
[]interface{}{
|
|
map[string]interface{}{
|
|
"avg": float64(0.278),
|
|
- "hr": int(65),
|
|
+ "hr": float64(65),
|
|
"name": string("Mark McGwire"),
|
|
},
|
|
map[string]interface{}{
|
|
"avg": float64(0.288),
|
|
- "hr": int(63),
|
|
+ "hr": float64(63),
|
|
"name": string("Sammy Sosa"),
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: map[*int]string{
|
|
new(int): "hello",
|
|
},
|
|
y: map[*int]string{
|
|
new(int): "world",
|
|
},
|
|
wantDiff: `
|
|
map[*int]string{
|
|
- ⟪0xdeadf00f⟫: "hello",
|
|
+ ⟪0xdeadf00f⟫: "world",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: intPtr(0),
|
|
y: intPtr(0),
|
|
opts: []cmp.Option{
|
|
cmp.Comparer(func(x, y *int) bool { return x == y }),
|
|
},
|
|
// TODO: This output is unhelpful and should show the address.
|
|
wantDiff: `
|
|
(*int)(
|
|
- &0,
|
|
+ &0,
|
|
)
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: [2][]int{
|
|
{0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 0, 9, 0, 0},
|
|
{0, 1, 0, 0, 0, 20},
|
|
},
|
|
y: [2][]int{
|
|
{1, 2, 3, 0, 4, 5, 6, 7, 0, 8, 9, 0, 0, 0},
|
|
{0, 0, 1, 2, 0, 0, 0},
|
|
},
|
|
opts: []cmp.Option{
|
|
cmp.FilterPath(func(p cmp.Path) bool {
|
|
vx, vy := p.Last().Values()
|
|
if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 {
|
|
return true
|
|
}
|
|
if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}, cmp.Ignore()),
|
|
},
|
|
wantDiff: `
|
|
[2][]int{
|
|
{..., 1, 2, 3, ..., 4, 5, 6, 7, ..., 8, ..., 9, ...},
|
|
{
|
|
... // 6 ignored and 1 identical elements
|
|
- 20,
|
|
+ 2,
|
|
... // 3 ignored elements
|
|
},
|
|
}
|
|
`,
|
|
reason: "all zero slice elements are ignored (even if missing)",
|
|
}, {
|
|
label: label,
|
|
x: [2]map[string]int{
|
|
{"ignore1": 0, "ignore2": 0, "keep1": 1, "keep2": 2, "KEEP3": 3, "IGNORE3": 0},
|
|
{"keep1": 1, "ignore1": 0},
|
|
},
|
|
y: [2]map[string]int{
|
|
{"ignore1": 0, "ignore3": 0, "ignore4": 0, "keep1": 1, "keep2": 2, "KEEP3": 3},
|
|
{"keep1": 1, "keep2": 2, "ignore2": 0},
|
|
},
|
|
opts: []cmp.Option{
|
|
cmp.FilterPath(func(p cmp.Path) bool {
|
|
vx, vy := p.Last().Values()
|
|
if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 {
|
|
return true
|
|
}
|
|
if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}, cmp.Ignore()),
|
|
},
|
|
wantDiff: `
|
|
[2]map[string]int{
|
|
{"KEEP3": 3, "keep1": 1, "keep2": 2, ...},
|
|
{
|
|
... // 2 ignored entries
|
|
"keep1": 1,
|
|
+ "keep2": 2,
|
|
},
|
|
}
|
|
`,
|
|
reason: "all zero map entries are ignored (even if missing)",
|
|
}}
|
|
}
|
|
|
|
func transformerTests() []test {
|
|
type StringBytes struct {
|
|
String string
|
|
Bytes []byte
|
|
}
|
|
|
|
const label = "Transformer"
|
|
|
|
transformOnce := func(name string, f interface{}) cmp.Option {
|
|
xform := cmp.Transformer(name, f)
|
|
return cmp.FilterPath(func(p cmp.Path) bool {
|
|
for _, ps := range p {
|
|
if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}, xform)
|
|
}
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: uint8(0),
|
|
y: uint8(1),
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("λ", func(in uint8) uint16 { return uint16(in) }),
|
|
cmp.Transformer("λ", func(in uint16) uint32 { return uint32(in) }),
|
|
cmp.Transformer("λ", func(in uint32) uint64 { return uint64(in) }),
|
|
},
|
|
wantDiff: `
|
|
uint8(Inverse(λ, uint16(Inverse(λ, uint32(Inverse(λ, uint64(
|
|
- 0x00,
|
|
+ 0x01,
|
|
)))))))
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: 0,
|
|
y: 1,
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("λ", func(in int) int { return in / 2 }),
|
|
cmp.Transformer("λ", func(in int) int { return in }),
|
|
},
|
|
wantPanic: "ambiguous set of applicable options",
|
|
}, {
|
|
label: label,
|
|
x: []int{0, -5, 0, -1},
|
|
y: []int{1, 3, 0, -5},
|
|
opts: []cmp.Option{
|
|
cmp.FilterValues(
|
|
func(x, y int) bool { return x+y >= 0 },
|
|
cmp.Transformer("λ", func(in int) int64 { return int64(in / 2) }),
|
|
),
|
|
cmp.FilterValues(
|
|
func(x, y int) bool { return x+y < 0 },
|
|
cmp.Transformer("λ", func(in int) int64 { return int64(in) }),
|
|
),
|
|
},
|
|
wantDiff: `
|
|
[]int{
|
|
Inverse(λ, int64(0)),
|
|
- Inverse(λ, int64(-5)),
|
|
+ Inverse(λ, int64(3)),
|
|
Inverse(λ, int64(0)),
|
|
- Inverse(λ, int64(-1)),
|
|
+ Inverse(λ, int64(-5)),
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: 0,
|
|
y: 1,
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("λ", func(in int) interface{} {
|
|
if in == 0 {
|
|
return "zero"
|
|
}
|
|
return float64(in)
|
|
}),
|
|
},
|
|
wantDiff: `
|
|
int(Inverse(λ, interface{}(
|
|
- string("zero"),
|
|
+ float64(1),
|
|
)))
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: `{
|
|
"firstName": "John",
|
|
"lastName": "Smith",
|
|
"age": 25,
|
|
"isAlive": true,
|
|
"address": {
|
|
"city": "Los Angeles",
|
|
"postalCode": "10021-3100",
|
|
"state": "CA",
|
|
"streetAddress": "21 2nd Street"
|
|
},
|
|
"phoneNumbers": [{
|
|
"type": "home",
|
|
"number": "212 555-4321"
|
|
},{
|
|
"type": "office",
|
|
"number": "646 555-4567"
|
|
},{
|
|
"number": "123 456-7890",
|
|
"type": "mobile"
|
|
}],
|
|
"children": []
|
|
}`,
|
|
y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,
|
|
"address":{"streetAddress":"21 2nd Street","city":"New York",
|
|
"state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home",
|
|
"number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{
|
|
"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`,
|
|
opts: []cmp.Option{
|
|
transformOnce("ParseJSON", func(s string) (m map[string]interface{}) {
|
|
if err := json.Unmarshal([]byte(s), &m); err != nil {
|
|
panic(err)
|
|
}
|
|
return m
|
|
}),
|
|
},
|
|
wantDiff: `
|
|
string(Inverse(ParseJSON, map[string]interface{}{
|
|
"address": map[string]interface{}{
|
|
- "city": string("Los Angeles"),
|
|
+ "city": string("New York"),
|
|
"postalCode": string("10021-3100"),
|
|
- "state": string("CA"),
|
|
+ "state": string("NY"),
|
|
"streetAddress": string("21 2nd Street"),
|
|
},
|
|
"age": float64(25),
|
|
"children": []interface{}{},
|
|
"firstName": string("John"),
|
|
"isAlive": bool(true),
|
|
"lastName": string("Smith"),
|
|
"phoneNumbers": []interface{}{
|
|
map[string]interface{}{
|
|
- "number": string("212 555-4321"),
|
|
+ "number": string("212 555-1234"),
|
|
"type": string("home"),
|
|
},
|
|
map[string]interface{}{"number": string("646 555-4567"), "type": string("office")},
|
|
map[string]interface{}{"number": string("123 456-7890"), "type": string("mobile")},
|
|
},
|
|
+ "spouse": nil,
|
|
}))
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")},
|
|
y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")},
|
|
opts: []cmp.Option{
|
|
transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }),
|
|
transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }),
|
|
},
|
|
wantDiff: `
|
|
cmp_test.StringBytes{
|
|
String: Inverse(SplitString, []string{
|
|
"some",
|
|
"multi",
|
|
- "Line",
|
|
+ "line",
|
|
"string",
|
|
}),
|
|
Bytes: []uint8(Inverse(SplitBytes, [][]uint8{
|
|
{0x73, 0x6f, 0x6d, 0x65},
|
|
{0x6d, 0x75, 0x6c, 0x74, 0x69},
|
|
{0x6c, 0x69, 0x6e, 0x65},
|
|
{
|
|
- 0x62,
|
|
+ 0x42,
|
|
0x79,
|
|
0x74,
|
|
... // 2 identical elements
|
|
},
|
|
})),
|
|
}
|
|
`,
|
|
}, {
|
|
x: "a\nb\nc\n",
|
|
y: "a\nb\nc\n",
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }),
|
|
},
|
|
wantPanic: "recursive set of Transformers detected",
|
|
}, {
|
|
x: complex64(0),
|
|
y: complex64(0),
|
|
opts: []cmp.Option{
|
|
cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }),
|
|
cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }),
|
|
cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }),
|
|
},
|
|
wantPanic: "recursive set of Transformers detected",
|
|
}}
|
|
}
|
|
|
|
func reporterTests() []test {
|
|
const label = "Reporter"
|
|
|
|
type (
|
|
MyString string
|
|
MyByte byte
|
|
MyBytes []byte
|
|
MyInt int8
|
|
MyInts []int8
|
|
MyUint int16
|
|
MyUints []int16
|
|
MyFloat float32
|
|
MyFloats []float32
|
|
MyComposite struct {
|
|
StringA string
|
|
StringB MyString
|
|
BytesA []byte
|
|
BytesB []MyByte
|
|
BytesC MyBytes
|
|
IntsA []int8
|
|
IntsB []MyInt
|
|
IntsC MyInts
|
|
UintsA []uint16
|
|
UintsB []MyUint
|
|
UintsC MyUints
|
|
FloatsA []float32
|
|
FloatsB []MyFloat
|
|
FloatsC MyFloats
|
|
}
|
|
)
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: MyComposite{IntsA: []int8{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
|
|
y: MyComposite{IntsA: []int8{10, 11, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
|
|
wantDiff: `
|
|
cmp_test.MyComposite{
|
|
... // 3 identical fields
|
|
BytesB: nil,
|
|
BytesC: nil,
|
|
IntsA: []int8{
|
|
+ 10,
|
|
11,
|
|
- 12,
|
|
+ 21,
|
|
13,
|
|
14,
|
|
... // 15 identical elements
|
|
},
|
|
IntsB: nil,
|
|
IntsC: nil,
|
|
... // 6 identical fields
|
|
}
|
|
`,
|
|
reason: "unbatched diffing desired since few elements differ",
|
|
}, {
|
|
label: label,
|
|
x: MyComposite{IntsA: []int8{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
|
|
y: MyComposite{IntsA: []int8{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}},
|
|
wantDiff: `
|
|
cmp_test.MyComposite{
|
|
... // 3 identical fields
|
|
BytesB: nil,
|
|
BytesC: nil,
|
|
IntsA: []int8{
|
|
- 10, 11, 12, 13, 14, 15, 16,
|
|
+ 12, 29, 13, 27, 22, 23,
|
|
17, 18, 19, 20, 21,
|
|
- 22, 23, 24, 25, 26, 27, 28, 29,
|
|
+ 10, 26, 16, 25, 28, 11, 15, 24, 14,
|
|
},
|
|
IntsB: nil,
|
|
IntsC: nil,
|
|
... // 6 identical fields
|
|
}
|
|
`,
|
|
reason: "batched diffing desired since many elements differ",
|
|
}, {
|
|
label: label,
|
|
x: MyComposite{
|
|
BytesA: []byte{1, 2, 3},
|
|
BytesB: []MyByte{4, 5, 6},
|
|
BytesC: MyBytes{7, 8, 9},
|
|
IntsA: []int8{-1, -2, -3},
|
|
IntsB: []MyInt{-4, -5, -6},
|
|
IntsC: MyInts{-7, -8, -9},
|
|
UintsA: []uint16{1000, 2000, 3000},
|
|
UintsB: []MyUint{4000, 5000, 6000},
|
|
UintsC: MyUints{7000, 8000, 9000},
|
|
FloatsA: []float32{1.5, 2.5, 3.5},
|
|
FloatsB: []MyFloat{4.5, 5.5, 6.5},
|
|
FloatsC: MyFloats{7.5, 8.5, 9.5},
|
|
},
|
|
y: MyComposite{
|
|
BytesA: []byte{3, 2, 1},
|
|
BytesB: []MyByte{6, 5, 4},
|
|
BytesC: MyBytes{9, 8, 7},
|
|
IntsA: []int8{-3, -2, -1},
|
|
IntsB: []MyInt{-6, -5, -4},
|
|
IntsC: MyInts{-9, -8, -7},
|
|
UintsA: []uint16{3000, 2000, 1000},
|
|
UintsB: []MyUint{6000, 5000, 4000},
|
|
UintsC: MyUints{9000, 8000, 7000},
|
|
FloatsA: []float32{3.5, 2.5, 1.5},
|
|
FloatsB: []MyFloat{6.5, 5.5, 4.5},
|
|
FloatsC: MyFloats{9.5, 8.5, 7.5},
|
|
},
|
|
wantDiff: `
|
|
cmp_test.MyComposite{
|
|
StringA: "",
|
|
StringB: "",
|
|
BytesA: []uint8{
|
|
- 0x01, 0x02, 0x03, // -|...|
|
|
+ 0x03, 0x02, 0x01, // +|...|
|
|
},
|
|
BytesB: []cmp_test.MyByte{
|
|
- 0x04, 0x05, 0x06,
|
|
+ 0x06, 0x05, 0x04,
|
|
},
|
|
BytesC: cmp_test.MyBytes{
|
|
- 0x07, 0x08, 0x09, // -|...|
|
|
+ 0x09, 0x08, 0x07, // +|...|
|
|
},
|
|
IntsA: []int8{
|
|
- -1, -2, -3,
|
|
+ -3, -2, -1,
|
|
},
|
|
IntsB: []cmp_test.MyInt{
|
|
- -4, -5, -6,
|
|
+ -6, -5, -4,
|
|
},
|
|
IntsC: cmp_test.MyInts{
|
|
- -7, -8, -9,
|
|
+ -9, -8, -7,
|
|
},
|
|
UintsA: []uint16{
|
|
- 0x03e8, 0x07d0, 0x0bb8,
|
|
+ 0x0bb8, 0x07d0, 0x03e8,
|
|
},
|
|
UintsB: []cmp_test.MyUint{
|
|
- 4000, 5000, 6000,
|
|
+ 6000, 5000, 4000,
|
|
},
|
|
UintsC: cmp_test.MyUints{
|
|
- 7000, 8000, 9000,
|
|
+ 9000, 8000, 7000,
|
|
},
|
|
FloatsA: []float32{
|
|
- 1.5, 2.5, 3.5,
|
|
+ 3.5, 2.5, 1.5,
|
|
},
|
|
FloatsB: []cmp_test.MyFloat{
|
|
- 4.5, 5.5, 6.5,
|
|
+ 6.5, 5.5, 4.5,
|
|
},
|
|
FloatsC: cmp_test.MyFloats{
|
|
- 7.5, 8.5, 9.5,
|
|
+ 9.5, 8.5, 7.5,
|
|
},
|
|
}
|
|
`,
|
|
reason: "batched diffing available for both named and unnamed slices",
|
|
}, {
|
|
label: label,
|
|
x: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeX\x95A\xfd$fX\x8byT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1U~{\xf6\xb3~\x1dWi \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")},
|
|
y: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1u-[]]\xf6\xb3haha~\x1dWI \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")},
|
|
wantDiff: `
|
|
cmp_test.MyComposite{
|
|
StringA: "",
|
|
StringB: "",
|
|
BytesA: []uint8{
|
|
0xf3, 0x0f, 0x8a, 0xa4, 0xd3, 0x12, 0x52, 0x09, 0x24, 0xbe, // |......R.$.|
|
|
- 0x58, 0x95, 0x41, 0xfd, 0x24, 0x66, 0x58, 0x8b, 0x79, // -|X.A.$fX.y|
|
|
0x54, 0xac, 0x0d, 0xd8, 0x71, 0x77, 0x70, 0x20, 0x6a, 0x5c, 0x73, 0x7f, 0x8c, 0x17, 0x55, 0xc0, // |T...qwp j\s...U.|
|
|
0x34, 0xce, 0x6e, 0xf7, 0xaa, 0x47, 0xee, 0x32, 0x9d, 0xc5, 0xca, 0x1e, 0x58, 0xaf, 0x8f, 0x27, // |4.n..G.2....X..'|
|
|
0xf3, 0x02, 0x4a, 0x90, 0xed, 0x69, 0x2e, 0x70, 0x32, 0xb4, 0xab, 0x30, 0x20, 0xb6, 0xbd, 0x5c, // |..J..i.p2..0 ..\|
|
|
0x62, 0x34, 0x17, 0xb0, 0x00, 0xbb, 0x4f, 0x7e, 0x27, 0x47, 0x06, 0xf4, 0x2e, 0x66, 0xfd, 0x63, // |b4....O~'G...f.c|
|
|
0xd7, 0x04, 0xdd, 0xb7, 0x30, 0xb7, 0xd1, // |....0..|
|
|
- 0x55, 0x7e, 0x7b, 0xf6, 0xb3, 0x7e, 0x1d, 0x57, 0x69, // -|U~{..~.Wi|
|
|
+ 0x75, 0x2d, 0x5b, 0x5d, 0x5d, 0xf6, 0xb3, 0x68, 0x61, 0x68, 0x61, 0x7e, 0x1d, 0x57, 0x49, // +|u-[]]..haha~.WI|
|
|
0x20, 0x9e, 0xbc, 0xdf, 0xe1, 0x4d, 0xa9, 0xef, 0xa2, 0xd2, 0xed, 0xb4, 0x47, 0x78, 0xc9, 0xc9, // | ....M......Gx..|
|
|
0x27, 0xa4, 0xc6, 0xce, 0xec, 0x44, 0x70, 0x5d, // |'....Dp]|
|
|
},
|
|
BytesB: nil,
|
|
BytesC: nil,
|
|
... // 9 identical fields
|
|
}
|
|
`,
|
|
reason: "binary diff in hexdump form since data is binary data",
|
|
}, {
|
|
label: label,
|
|
x: MyComposite{StringB: MyString("readme.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000046\x0000000000000\x00011173\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
|
|
y: MyComposite{StringB: MyString("gopher.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000043\x0000000000000\x00011217\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
|
|
wantDiff: `
|
|
cmp_test.MyComposite{
|
|
StringA: "",
|
|
StringB: cmp_test.MyString{
|
|
- 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, // -|readme|
|
|
+ 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, // +|gopher|
|
|
0x2e, 0x74, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |.txt............|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................|
|
|
... // 64 identical bytes
|
|
0x30, 0x30, 0x36, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, // |00600.0000000.00|
|
|
0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, // |00000.0000000004|
|
|
- 0x36, // -|6|
|
|
+ 0x33, // +|3|
|
|
0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x31, 0x31, // |.00000000000.011|
|
|
- 0x31, 0x37, 0x33, // -|173|
|
|
+ 0x32, 0x31, 0x37, // +|217|
|
|
0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |. 0.............|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................|
|
|
... // 326 identical bytes
|
|
},
|
|
BytesA: nil,
|
|
BytesB: nil,
|
|
... // 10 identical fields
|
|
}
|
|
`,
|
|
reason: "binary diff desired since string looks like binary data",
|
|
}, {
|
|
label: label,
|
|
x: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"314 54th Avenue","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)},
|
|
y: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)},
|
|
wantDiff: strings.Replace(`
|
|
cmp_test.MyComposite{
|
|
StringA: "",
|
|
StringB: "",
|
|
BytesA: bytes.Join({
|
|
'{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"',
|
|
'address":{"streetAddress":"',
|
|
- "314 54th Avenue",
|
|
+ "21 2nd Street",
|
|
'","city":"New York","state":"NY","postalCode":"10021-3100"},"pho',
|
|
'neNumbers":[{"type":"home","number":"212 555-1234"},{"type":"off',
|
|
... // 101 identical bytes
|
|
}, ""),
|
|
BytesB: nil,
|
|
BytesC: nil,
|
|
... // 9 identical fields
|
|
}
|
|
`, "'", "`", -1),
|
|
reason: "batched textual diff desired since bytes looks like textual data",
|
|
}, {
|
|
label: label,
|
|
x: MyComposite{
|
|
StringA: strings.TrimPrefix(`
|
|
Package cmp determines equality of values.
|
|
|
|
This package is intended to be a more powerful and safer alternative to
|
|
reflect.DeepEqual for comparing whether two values are semantically equal.
|
|
|
|
The primary features of cmp are:
|
|
|
|
• When the default behavior of equality does not suit the needs of the test,
|
|
custom equality functions can override the equality operation.
|
|
For example, an equality function may report floats as equal so long as they
|
|
are within some tolerance of each other.
|
|
|
|
• Types that have an Equal method may use that method to determine equality.
|
|
This allows package authors to determine the equality operation for the types
|
|
that they define.
|
|
|
|
• If no custom equality functions are used and no Equal method is defined,
|
|
equality is determined by recursively comparing the primitive kinds on both
|
|
values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
|
|
fields are not compared by default; they result in panics unless suppressed
|
|
by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
|
|
using the AllowUnexported option.
|
|
`, "\n"),
|
|
},
|
|
y: MyComposite{
|
|
StringA: strings.TrimPrefix(`
|
|
Package cmp determines equality of value.
|
|
|
|
This package is intended to be a more powerful and safer alternative to
|
|
reflect.DeepEqual for comparing whether two values are semantically equal.
|
|
|
|
The primary features of cmp are:
|
|
|
|
• When the default behavior of equality does not suit the needs of the test,
|
|
custom equality functions can override the equality operation.
|
|
For example, an equality function may report floats as equal so long as they
|
|
are within some tolerance of each other.
|
|
|
|
• If no custom equality functions are used and no Equal method is defined,
|
|
equality is determined by recursively comparing the primitive kinds on both
|
|
values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
|
|
fields are not compared by default; they result in panics unless suppressed
|
|
by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
|
|
using the AllowUnexported option.`, "\n"),
|
|
},
|
|
wantDiff: `
|
|
cmp_test.MyComposite{
|
|
StringA: strings.Join({
|
|
- "Package cmp determines equality of values.",
|
|
+ "Package cmp determines equality of value.",
|
|
"",
|
|
"This package is intended to be a more powerful and safer alternative to",
|
|
... // 6 identical lines
|
|
"For example, an equality function may report floats as equal so long as they",
|
|
"are within some tolerance of each other.",
|
|
- "",
|
|
- "• Types that have an Equal method may use that method to determine equality.",
|
|
- "This allows package authors to determine the equality operation for the types",
|
|
- "that they define.",
|
|
"",
|
|
"• If no custom equality functions are used and no Equal method is defined,",
|
|
... // 3 identical lines
|
|
"by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared",
|
|
"using the AllowUnexported option.",
|
|
- "",
|
|
}, "\n"),
|
|
StringB: "",
|
|
BytesA: nil,
|
|
... // 11 identical fields
|
|
}
|
|
`,
|
|
reason: "batched per-line diff desired since string looks like multi-line textual data",
|
|
}}
|
|
}
|
|
|
|
func embeddedTests() []test {
|
|
const label = "EmbeddedStruct/"
|
|
|
|
privateStruct := *new(ts.ParentStructA).PrivateStruct()
|
|
|
|
createStructA := func(i int) ts.ParentStructA {
|
|
s := ts.ParentStructA{}
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
return s
|
|
}
|
|
|
|
createStructB := func(i int) ts.ParentStructB {
|
|
s := ts.ParentStructB{}
|
|
s.PublicStruct.Public = 1 + i
|
|
s.PublicStruct.SetPrivate(2 + i)
|
|
return s
|
|
}
|
|
|
|
createStructC := func(i int) ts.ParentStructC {
|
|
s := ts.ParentStructC{}
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
s.Public = 3 + i
|
|
s.SetPrivate(4 + i)
|
|
return s
|
|
}
|
|
|
|
createStructD := func(i int) ts.ParentStructD {
|
|
s := ts.ParentStructD{}
|
|
s.PublicStruct.Public = 1 + i
|
|
s.PublicStruct.SetPrivate(2 + i)
|
|
s.Public = 3 + i
|
|
s.SetPrivate(4 + i)
|
|
return s
|
|
}
|
|
|
|
createStructE := func(i int) ts.ParentStructE {
|
|
s := ts.ParentStructE{}
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
s.PublicStruct.Public = 3 + i
|
|
s.PublicStruct.SetPrivate(4 + i)
|
|
return s
|
|
}
|
|
|
|
createStructF := func(i int) ts.ParentStructF {
|
|
s := ts.ParentStructF{}
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
s.PublicStruct.Public = 3 + i
|
|
s.PublicStruct.SetPrivate(4 + i)
|
|
s.Public = 5 + i
|
|
s.SetPrivate(6 + i)
|
|
return s
|
|
}
|
|
|
|
createStructG := func(i int) *ts.ParentStructG {
|
|
s := ts.NewParentStructG()
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
return s
|
|
}
|
|
|
|
createStructH := func(i int) *ts.ParentStructH {
|
|
s := ts.NewParentStructH()
|
|
s.PublicStruct.Public = 1 + i
|
|
s.PublicStruct.SetPrivate(2 + i)
|
|
return s
|
|
}
|
|
|
|
createStructI := func(i int) *ts.ParentStructI {
|
|
s := ts.NewParentStructI()
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
s.PublicStruct.Public = 3 + i
|
|
s.PublicStruct.SetPrivate(4 + i)
|
|
return s
|
|
}
|
|
|
|
createStructJ := func(i int) *ts.ParentStructJ {
|
|
s := ts.NewParentStructJ()
|
|
s.PrivateStruct().Public = 1 + i
|
|
s.PrivateStruct().SetPrivate(2 + i)
|
|
s.PublicStruct.Public = 3 + i
|
|
s.PublicStruct.SetPrivate(4 + i)
|
|
s.Private().Public = 5 + i
|
|
s.Private().SetPrivate(6 + i)
|
|
s.Public.Public = 7 + i
|
|
s.Public.SetPrivate(8 + i)
|
|
return s
|
|
}
|
|
|
|
// TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/21122).
|
|
wantPanicNotGo110 := func(s string) string {
|
|
if !flags.AtLeastGo110 {
|
|
return ""
|
|
}
|
|
return s
|
|
}
|
|
|
|
return []test{{
|
|
label: label + "ParentStructA",
|
|
x: ts.ParentStructA{},
|
|
y: ts.ParentStructA{},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructA",
|
|
x: ts.ParentStructA{},
|
|
y: ts.ParentStructA{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructA{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructA",
|
|
x: createStructA(0),
|
|
y: createStructA(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructA{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructA",
|
|
x: createStructA(0),
|
|
y: createStructA(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructA",
|
|
x: createStructA(0),
|
|
y: createStructA(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
teststructs.ParentStructA{
|
|
privateStruct: teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructB",
|
|
x: ts.ParentStructB{},
|
|
y: ts.ParentStructB{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructB{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructB",
|
|
x: ts.ParentStructB{},
|
|
y: ts.ParentStructB{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructB{}),
|
|
cmpopts.IgnoreUnexported(ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructB",
|
|
x: createStructB(0),
|
|
y: createStructB(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructB{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructB",
|
|
x: createStructB(0),
|
|
y: createStructB(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructB",
|
|
x: createStructB(0),
|
|
y: createStructB(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
|
|
},
|
|
wantDiff: `
|
|
teststructs.ParentStructB{
|
|
PublicStruct: teststructs.PublicStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructC",
|
|
x: ts.ParentStructC{},
|
|
y: ts.ParentStructC{},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructC",
|
|
x: ts.ParentStructC{},
|
|
y: ts.ParentStructC{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructC{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructC",
|
|
x: createStructC(0),
|
|
y: createStructC(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructC{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructC",
|
|
x: createStructC(0),
|
|
y: createStructC(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructC",
|
|
x: createStructC(0),
|
|
y: createStructC(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
teststructs.ParentStructC{
|
|
privateStruct: teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
- Public: 3,
|
|
+ Public: 4,
|
|
- private: 4,
|
|
+ private: 5,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructD",
|
|
x: ts.ParentStructD{},
|
|
y: ts.ParentStructD{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructD{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructD",
|
|
x: ts.ParentStructD{},
|
|
y: ts.ParentStructD{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructD{}),
|
|
cmpopts.IgnoreUnexported(ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructD",
|
|
x: createStructD(0),
|
|
y: createStructD(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructD{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructD",
|
|
x: createStructD(0),
|
|
y: createStructD(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructD",
|
|
x: createStructD(0),
|
|
y: createStructD(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
|
|
},
|
|
wantDiff: `
|
|
teststructs.ParentStructD{
|
|
PublicStruct: teststructs.PublicStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
- Public: 3,
|
|
+ Public: 4,
|
|
- private: 4,
|
|
+ private: 5,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructE",
|
|
x: ts.ParentStructE{},
|
|
y: ts.ParentStructE{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructE{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructE",
|
|
x: ts.ParentStructE{},
|
|
y: ts.ParentStructE{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructE{}),
|
|
cmpopts.IgnoreUnexported(ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructE",
|
|
x: createStructE(0),
|
|
y: createStructE(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructE{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructE",
|
|
x: createStructE(0),
|
|
y: createStructE(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructE",
|
|
x: createStructE(0),
|
|
y: createStructE(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructE",
|
|
x: createStructE(0),
|
|
y: createStructE(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
teststructs.ParentStructE{
|
|
privateStruct: teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
PublicStruct: teststructs.PublicStruct{
|
|
- Public: 3,
|
|
+ Public: 4,
|
|
- private: 4,
|
|
+ private: 5,
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructF",
|
|
x: ts.ParentStructF{},
|
|
y: ts.ParentStructF{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructF{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructF",
|
|
x: ts.ParentStructF{},
|
|
y: ts.ParentStructF{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructF{}),
|
|
cmpopts.IgnoreUnexported(ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructF",
|
|
x: createStructF(0),
|
|
y: createStructF(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructF{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructF",
|
|
x: createStructF(0),
|
|
y: createStructF(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructF",
|
|
x: createStructF(0),
|
|
y: createStructF(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructF",
|
|
x: createStructF(0),
|
|
y: createStructF(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
teststructs.ParentStructF{
|
|
privateStruct: teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
PublicStruct: teststructs.PublicStruct{
|
|
- Public: 3,
|
|
+ Public: 4,
|
|
- private: 4,
|
|
+ private: 5,
|
|
},
|
|
- Public: 5,
|
|
+ Public: 6,
|
|
- private: 6,
|
|
+ private: 7,
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructG",
|
|
x: ts.ParentStructG{},
|
|
y: ts.ParentStructG{},
|
|
wantPanic: wantPanicNotGo110("cannot handle unexported field"),
|
|
}, {
|
|
label: label + "ParentStructG",
|
|
x: ts.ParentStructG{},
|
|
y: ts.ParentStructG{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructG{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructG",
|
|
x: createStructG(0),
|
|
y: createStructG(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructG{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructG",
|
|
x: createStructG(0),
|
|
y: createStructG(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructG",
|
|
x: createStructG(0),
|
|
y: createStructG(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
&teststructs.ParentStructG{
|
|
privateStruct: &teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructH",
|
|
x: ts.ParentStructH{},
|
|
y: ts.ParentStructH{},
|
|
}, {
|
|
label: label + "ParentStructH",
|
|
x: createStructH(0),
|
|
y: createStructH(0),
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructH",
|
|
x: ts.ParentStructH{},
|
|
y: ts.ParentStructH{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructH{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructH",
|
|
x: createStructH(0),
|
|
y: createStructH(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructH{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructH",
|
|
x: createStructH(0),
|
|
y: createStructH(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructH",
|
|
x: createStructH(0),
|
|
y: createStructH(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
|
|
},
|
|
wantDiff: `
|
|
&teststructs.ParentStructH{
|
|
PublicStruct: &teststructs.PublicStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: ts.ParentStructI{},
|
|
y: ts.ParentStructI{},
|
|
wantPanic: wantPanicNotGo110("cannot handle unexported field"),
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: ts.ParentStructI{},
|
|
y: ts.ParentStructI{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructI{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: createStructI(0),
|
|
y: createStructI(0),
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructI{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: createStructI(0),
|
|
y: createStructI(0),
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: createStructI(0),
|
|
y: createStructI(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructI{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: createStructI(0),
|
|
y: createStructI(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructI",
|
|
x: createStructI(0),
|
|
y: createStructI(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
&teststructs.ParentStructI{
|
|
privateStruct: &teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
PublicStruct: &teststructs.PublicStruct{
|
|
- Public: 3,
|
|
+ Public: 4,
|
|
- private: 4,
|
|
+ private: 5,
|
|
},
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "ParentStructJ",
|
|
x: ts.ParentStructJ{},
|
|
y: ts.ParentStructJ{},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructJ",
|
|
x: ts.ParentStructJ{},
|
|
y: ts.ParentStructJ{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructJ{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructJ",
|
|
x: ts.ParentStructJ{},
|
|
y: ts.ParentStructJ{},
|
|
opts: []cmp.Option{
|
|
cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructJ",
|
|
x: createStructJ(0),
|
|
y: createStructJ(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
|
|
},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label + "ParentStructJ",
|
|
x: createStructJ(0),
|
|
y: createStructJ(0),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
}, {
|
|
label: label + "ParentStructJ",
|
|
x: createStructJ(0),
|
|
y: createStructJ(1),
|
|
opts: []cmp.Option{
|
|
cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
|
|
},
|
|
wantDiff: `
|
|
&teststructs.ParentStructJ{
|
|
privateStruct: &teststructs.privateStruct{
|
|
- Public: 1,
|
|
+ Public: 2,
|
|
- private: 2,
|
|
+ private: 3,
|
|
},
|
|
PublicStruct: &teststructs.PublicStruct{
|
|
- Public: 3,
|
|
+ Public: 4,
|
|
- private: 4,
|
|
+ private: 5,
|
|
},
|
|
Public: teststructs.PublicStruct{
|
|
- Public: 7,
|
|
+ Public: 8,
|
|
- private: 8,
|
|
+ private: 9,
|
|
},
|
|
private: teststructs.privateStruct{
|
|
- Public: 5,
|
|
+ Public: 6,
|
|
- private: 6,
|
|
+ private: 7,
|
|
},
|
|
}
|
|
`,
|
|
}}
|
|
}
|
|
|
|
func methodTests() []test {
|
|
const label = "EqualMethod/"
|
|
|
|
// A common mistake that the Equal method is on a pointer receiver,
|
|
// but only a non-pointer value is present in the struct.
|
|
// A transform can be used to forcibly reference the value.
|
|
derefTransform := cmp.FilterPath(func(p cmp.Path) bool {
|
|
if len(p) == 0 {
|
|
return false
|
|
}
|
|
t := p[len(p)-1].Type()
|
|
if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr {
|
|
return false
|
|
}
|
|
if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok {
|
|
tf := m.Func.Type()
|
|
return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 &&
|
|
tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true)
|
|
}
|
|
return false
|
|
}, cmp.Transformer("Ref", func(x interface{}) interface{} {
|
|
v := reflect.ValueOf(x)
|
|
vp := reflect.New(v.Type())
|
|
vp.Elem().Set(v)
|
|
return vp.Interface()
|
|
}))
|
|
|
|
// For each of these types, there is an Equal method defined, which always
|
|
// returns true, while the underlying data are fundamentally different.
|
|
// Since the method should be called, these are expected to be equal.
|
|
return []test{{
|
|
label: label + "StructA",
|
|
x: ts.StructA{X: "NotEqual"},
|
|
y: ts.StructA{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructA",
|
|
x: &ts.StructA{X: "NotEqual"},
|
|
y: &ts.StructA{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructB",
|
|
x: ts.StructB{X: "NotEqual"},
|
|
y: ts.StructB{X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructB{
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructB",
|
|
x: ts.StructB{X: "NotEqual"},
|
|
y: ts.StructB{X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructB",
|
|
x: &ts.StructB{X: "NotEqual"},
|
|
y: &ts.StructB{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructC",
|
|
x: ts.StructC{X: "NotEqual"},
|
|
y: ts.StructC{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructC",
|
|
x: &ts.StructC{X: "NotEqual"},
|
|
y: &ts.StructC{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructD",
|
|
x: ts.StructD{X: "NotEqual"},
|
|
y: ts.StructD{X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructD{
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructD",
|
|
x: ts.StructD{X: "NotEqual"},
|
|
y: ts.StructD{X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructD",
|
|
x: &ts.StructD{X: "NotEqual"},
|
|
y: &ts.StructD{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructE",
|
|
x: ts.StructE{X: "NotEqual"},
|
|
y: ts.StructE{X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructE{
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructE",
|
|
x: ts.StructE{X: "NotEqual"},
|
|
y: ts.StructE{X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructE",
|
|
x: &ts.StructE{X: "NotEqual"},
|
|
y: &ts.StructE{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructF",
|
|
x: ts.StructF{X: "NotEqual"},
|
|
y: ts.StructF{X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructF{
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructF",
|
|
x: &ts.StructF{X: "NotEqual"},
|
|
y: &ts.StructF{X: "not_equal"},
|
|
}, {
|
|
label: label + "StructA1",
|
|
x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
|
|
y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
|
|
}, {
|
|
label: label + "StructA1",
|
|
x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructA1{
|
|
StructA: teststructs.StructA{X: "NotEqual"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructA1",
|
|
x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
|
|
y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
|
|
}, {
|
|
label: label + "StructA1",
|
|
x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
&teststructs.StructA1{
|
|
StructA: teststructs.StructA{X: "NotEqual"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructB1",
|
|
x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
|
|
y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructB1",
|
|
x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
wantDiff: `
|
|
teststructs.StructB1{
|
|
StructB: teststructs.StructB(Inverse(Ref, &teststructs.StructB{X: "NotEqual"})),
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructB1",
|
|
x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
|
|
y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructB1",
|
|
x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
wantDiff: `
|
|
&teststructs.StructB1{
|
|
StructB: teststructs.StructB(Inverse(Ref, &teststructs.StructB{X: "NotEqual"})),
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructC1",
|
|
x: ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructC1",
|
|
x: &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructD1",
|
|
x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructD1{
|
|
- StructD: teststructs.StructD{X: "NotEqual"},
|
|
+ StructD: teststructs.StructD{X: "not_equal"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructD1",
|
|
x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructD1",
|
|
x: &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructE1",
|
|
x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructE1{
|
|
- StructE: teststructs.StructE{X: "NotEqual"},
|
|
+ StructE: teststructs.StructE{X: "not_equal"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructE1",
|
|
x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
|
|
opts: []cmp.Option{derefTransform},
|
|
}, {
|
|
label: label + "StructE1",
|
|
x: &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructF1",
|
|
x: ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructF1{
|
|
- StructF: teststructs.StructF{X: "NotEqual"},
|
|
+ StructF: teststructs.StructF{X: "not_equal"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructF1",
|
|
x: &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructA2",
|
|
x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
|
|
y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
|
|
}, {
|
|
label: label + "StructA2",
|
|
x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructA2{
|
|
StructA: &teststructs.StructA{X: "NotEqual"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructA2",
|
|
x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
|
|
y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
|
|
}, {
|
|
label: label + "StructA2",
|
|
x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
&teststructs.StructA2{
|
|
StructA: &teststructs.StructA{X: "NotEqual"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructB2",
|
|
x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
|
|
y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
|
|
}, {
|
|
label: label + "StructB2",
|
|
x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructB2{
|
|
StructB: &teststructs.StructB{X: "NotEqual"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructB2",
|
|
x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
|
|
y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
|
|
}, {
|
|
label: label + "StructB2",
|
|
x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
|
|
wantDiff: `
|
|
&teststructs.StructB2{
|
|
StructB: &teststructs.StructB{X: "NotEqual"},
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "StructC2",
|
|
x: ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructC2",
|
|
x: &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructD2",
|
|
x: ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructD2",
|
|
x: &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructE2",
|
|
x: ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructE2",
|
|
x: &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructF2",
|
|
x: ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
|
|
y: ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructF2",
|
|
x: &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
|
|
y: &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
|
|
}, {
|
|
label: label + "StructNo",
|
|
x: ts.StructNo{X: "NotEqual"},
|
|
y: ts.StructNo{X: "not_equal"},
|
|
wantDiff: `
|
|
teststructs.StructNo{
|
|
- X: "NotEqual",
|
|
+ X: "not_equal",
|
|
}
|
|
`,
|
|
}, {
|
|
label: label + "AssignA",
|
|
x: ts.AssignA(func() int { return 0 }),
|
|
y: ts.AssignA(func() int { return 1 }),
|
|
}, {
|
|
label: label + "AssignB",
|
|
x: ts.AssignB(struct{ A int }{0}),
|
|
y: ts.AssignB(struct{ A int }{1}),
|
|
}, {
|
|
label: label + "AssignC",
|
|
x: ts.AssignC(make(chan bool)),
|
|
y: ts.AssignC(make(chan bool)),
|
|
}, {
|
|
label: label + "AssignD",
|
|
x: ts.AssignD(make(chan bool)),
|
|
y: ts.AssignD(make(chan bool)),
|
|
}}
|
|
}
|
|
|
|
func project1Tests() []test {
|
|
const label = "Project1"
|
|
|
|
ignoreUnexported := cmpopts.IgnoreUnexported(
|
|
ts.EagleImmutable{},
|
|
ts.DreamerImmutable{},
|
|
ts.SlapImmutable{},
|
|
ts.GoatImmutable{},
|
|
ts.DonkeyImmutable{},
|
|
ts.LoveRadius{},
|
|
ts.SummerLove{},
|
|
ts.SummerLoveSummary{},
|
|
)
|
|
|
|
createEagle := func() ts.Eagle {
|
|
return ts.Eagle{
|
|
Name: "eagle",
|
|
Hounds: []string{"buford", "tannen"},
|
|
Desc: "some description",
|
|
Dreamers: []ts.Dreamer{{}, {
|
|
Name: "dreamer2",
|
|
Animal: []interface{}{
|
|
ts.Goat{
|
|
Target: "corporation",
|
|
Immutable: &ts.GoatImmutable{
|
|
ID: "southbay",
|
|
State: (*pb.Goat_States)(intPtr(5)),
|
|
Started: now,
|
|
},
|
|
},
|
|
ts.Donkey{},
|
|
},
|
|
Amoeba: 53,
|
|
}},
|
|
Slaps: []ts.Slap{{
|
|
Name: "slapID",
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
|
|
Immutable: &ts.SlapImmutable{
|
|
ID: "immutableSlap",
|
|
MildSlap: true,
|
|
Started: now,
|
|
LoveRadius: &ts.LoveRadius{
|
|
Summer: &ts.SummerLove{
|
|
Summary: &ts.SummerLoveSummary{
|
|
Devices: []string{"foo", "bar", "baz"},
|
|
ChangeType: []pb.SummerType{1, 2, 3},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}},
|
|
Immutable: &ts.EagleImmutable{
|
|
ID: "eagleID",
|
|
Birthday: now,
|
|
MissingCall: (*pb.Eagle_MissingCalls)(intPtr(55)),
|
|
},
|
|
}
|
|
}
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: ts.Eagle{Slaps: []ts.Slap{{
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
|
|
}}},
|
|
y: ts.Eagle{Slaps: []ts.Slap{{
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
|
|
}}},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: ts.Eagle{Slaps: []ts.Slap{{
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
|
|
}}},
|
|
y: ts.Eagle{Slaps: []ts.Slap{{
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
|
|
}}},
|
|
opts: []cmp.Option{cmp.Comparer(pb.Equal)},
|
|
}, {
|
|
label: label,
|
|
x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
|
|
}}},
|
|
y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
|
|
Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}},
|
|
}}},
|
|
opts: []cmp.Option{cmp.Comparer(pb.Equal)},
|
|
wantDiff: `
|
|
teststructs.Eagle{
|
|
... // 4 identical fields
|
|
Dreamers: nil,
|
|
Prong: 0,
|
|
Slaps: []teststructs.Slap{
|
|
... // 2 identical elements
|
|
{},
|
|
{},
|
|
{
|
|
Name: "",
|
|
Desc: "",
|
|
DescLong: "",
|
|
- Args: s"metadata",
|
|
+ Args: s"metadata2",
|
|
Tense: 0,
|
|
Interval: 0,
|
|
... // 3 identical fields
|
|
},
|
|
},
|
|
StateGoverner: "",
|
|
PrankRating: "",
|
|
... // 2 identical fields
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: createEagle(),
|
|
y: createEagle(),
|
|
opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
|
|
}, {
|
|
label: label,
|
|
x: func() ts.Eagle {
|
|
eg := createEagle()
|
|
eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2"
|
|
eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(intPtr(6))
|
|
eg.Slaps[0].Immutable.MildSlap = false
|
|
return eg
|
|
}(),
|
|
y: func() ts.Eagle {
|
|
eg := createEagle()
|
|
devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices
|
|
eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1]
|
|
return eg
|
|
}(),
|
|
opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
|
|
wantDiff: `
|
|
teststructs.Eagle{
|
|
... // 2 identical fields
|
|
Desc: "some description",
|
|
DescLong: "",
|
|
Dreamers: []teststructs.Dreamer{
|
|
{},
|
|
{
|
|
... // 4 identical fields
|
|
ContSlaps: nil,
|
|
ContSlapsInterval: 0,
|
|
Animal: []interface{}{
|
|
teststructs.Goat{
|
|
Target: "corporation",
|
|
Slaps: nil,
|
|
FunnyPrank: "",
|
|
Immutable: &teststructs.GoatImmutable{
|
|
- ID: "southbay2",
|
|
+ ID: "southbay",
|
|
- State: &6,
|
|
+ State: &5,
|
|
Started: s"2009-11-10 23:00:00 +0000 UTC",
|
|
Stopped: s"0001-01-01 00:00:00 +0000 UTC",
|
|
... // 1 ignored and 1 identical fields
|
|
},
|
|
},
|
|
teststructs.Donkey{},
|
|
},
|
|
Ornamental: false,
|
|
Amoeba: 53,
|
|
... // 5 identical fields
|
|
},
|
|
},
|
|
Prong: 0,
|
|
Slaps: []teststructs.Slap{
|
|
{
|
|
... // 6 identical fields
|
|
Homeland: 0x00,
|
|
FunnyPrank: "",
|
|
Immutable: &teststructs.SlapImmutable{
|
|
ID: "immutableSlap",
|
|
Out: nil,
|
|
- MildSlap: false,
|
|
+ MildSlap: true,
|
|
PrettyPrint: "",
|
|
State: nil,
|
|
Started: s"2009-11-10 23:00:00 +0000 UTC",
|
|
Stopped: s"0001-01-01 00:00:00 +0000 UTC",
|
|
LastUpdate: s"0001-01-01 00:00:00 +0000 UTC",
|
|
LoveRadius: &teststructs.LoveRadius{
|
|
Summer: &teststructs.SummerLove{
|
|
Summary: &teststructs.SummerLoveSummary{
|
|
Devices: []string{
|
|
"foo",
|
|
- "bar",
|
|
- "baz",
|
|
},
|
|
ChangeType: []testprotos.SummerType{1, 2, 3},
|
|
... // 1 ignored field
|
|
},
|
|
... // 1 ignored field
|
|
},
|
|
... // 1 ignored field
|
|
},
|
|
... // 1 ignored field
|
|
},
|
|
},
|
|
},
|
|
StateGoverner: "",
|
|
PrankRating: "",
|
|
... // 2 identical fields
|
|
}
|
|
`,
|
|
}}
|
|
}
|
|
|
|
type germSorter []*pb.Germ
|
|
|
|
func (gs germSorter) Len() int { return len(gs) }
|
|
func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() }
|
|
func (gs germSorter) Swap(i, j int) { gs[i], gs[j] = gs[j], gs[i] }
|
|
|
|
func project2Tests() []test {
|
|
const label = "Project2"
|
|
|
|
sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ {
|
|
out := append([]*pb.Germ(nil), in...) // Make copy
|
|
sort.Sort(germSorter(out))
|
|
return out
|
|
})
|
|
|
|
equalDish := cmp.Comparer(func(x, y *ts.Dish) bool {
|
|
if x == nil || y == nil {
|
|
return x == nil && y == nil
|
|
}
|
|
px, err1 := x.Proto()
|
|
py, err2 := y.Proto()
|
|
if err1 != nil || err2 != nil {
|
|
return err1 == err2
|
|
}
|
|
return pb.Equal(px, py)
|
|
})
|
|
|
|
createBatch := func() ts.GermBatch {
|
|
return ts.GermBatch{
|
|
DirtyGerms: map[int32][]*pb.Germ{
|
|
17: {
|
|
{Stringer: pb.Stringer{X: "germ1"}},
|
|
},
|
|
18: {
|
|
{Stringer: pb.Stringer{X: "germ2"}},
|
|
{Stringer: pb.Stringer{X: "germ3"}},
|
|
{Stringer: pb.Stringer{X: "germ4"}},
|
|
},
|
|
},
|
|
GermMap: map[int32]*pb.Germ{
|
|
13: {Stringer: pb.Stringer{X: "germ13"}},
|
|
21: {Stringer: pb.Stringer{X: "germ21"}},
|
|
},
|
|
DishMap: map[int32]*ts.Dish{
|
|
0: ts.CreateDish(nil, io.EOF),
|
|
1: ts.CreateDish(nil, io.ErrUnexpectedEOF),
|
|
2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil),
|
|
},
|
|
HasPreviousResult: true,
|
|
DirtyID: 10,
|
|
GermStrain: 421,
|
|
InfectedAt: now,
|
|
}
|
|
}
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: createBatch(),
|
|
y: createBatch(),
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: createBatch(),
|
|
y: createBatch(),
|
|
opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
|
|
}, {
|
|
label: label,
|
|
x: createBatch(),
|
|
y: func() ts.GermBatch {
|
|
gb := createBatch()
|
|
s := gb.DirtyGerms[18]
|
|
s[0], s[1], s[2] = s[1], s[2], s[0]
|
|
return gb
|
|
}(),
|
|
opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish},
|
|
wantDiff: `
|
|
teststructs.GermBatch{
|
|
DirtyGerms: map[int32][]*testprotos.Germ{
|
|
17: {s"germ1"},
|
|
18: {
|
|
- s"germ2",
|
|
s"germ3",
|
|
s"germ4",
|
|
+ s"germ2",
|
|
},
|
|
},
|
|
CleanGerms: nil,
|
|
GermMap: map[int32]*testprotos.Germ{13: s"germ13", 21: s"germ21"},
|
|
... // 7 identical fields
|
|
}
|
|
`,
|
|
}, {
|
|
label: label,
|
|
x: createBatch(),
|
|
y: func() ts.GermBatch {
|
|
gb := createBatch()
|
|
s := gb.DirtyGerms[18]
|
|
s[0], s[1], s[2] = s[1], s[2], s[0]
|
|
return gb
|
|
}(),
|
|
opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
|
|
}, {
|
|
label: label,
|
|
x: func() ts.GermBatch {
|
|
gb := createBatch()
|
|
delete(gb.DirtyGerms, 17)
|
|
gb.DishMap[1] = nil
|
|
return gb
|
|
}(),
|
|
y: func() ts.GermBatch {
|
|
gb := createBatch()
|
|
gb.DirtyGerms[18] = gb.DirtyGerms[18][:2]
|
|
gb.GermStrain = 22
|
|
return gb
|
|
}(),
|
|
opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
|
|
wantDiff: `
|
|
teststructs.GermBatch{
|
|
DirtyGerms: map[int32][]*testprotos.Germ{
|
|
+ 17: {s"germ1"},
|
|
18: Inverse(Sort, []*testprotos.Germ{
|
|
s"germ2",
|
|
s"germ3",
|
|
- s"germ4",
|
|
}),
|
|
},
|
|
CleanGerms: nil,
|
|
GermMap: map[int32]*testprotos.Germ{13: s"germ13", 21: s"germ21"},
|
|
DishMap: map[int32]*teststructs.Dish{
|
|
0: &{err: &errors.errorString{s: "EOF"}},
|
|
- 1: nil,
|
|
+ 1: &{err: &errors.errorString{s: "unexpected EOF"}},
|
|
2: &{pb: &testprotos.Dish{Stringer: testprotos.Stringer{X: "dish"}}},
|
|
},
|
|
HasPreviousResult: true,
|
|
DirtyID: 10,
|
|
CleanID: 0,
|
|
- GermStrain: 421,
|
|
+ GermStrain: 22,
|
|
TotalDirtyGerms: 0,
|
|
InfectedAt: s"2009-11-10 23:00:00 +0000 UTC",
|
|
}
|
|
`,
|
|
}}
|
|
}
|
|
|
|
func project3Tests() []test {
|
|
const label = "Project3"
|
|
|
|
allowVisibility := cmp.AllowUnexported(ts.Dirt{})
|
|
|
|
ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{})
|
|
|
|
transformProtos := cmp.Transformer("λ", func(x pb.Dirt) *pb.Dirt {
|
|
return &x
|
|
})
|
|
|
|
equalTable := cmp.Comparer(func(x, y ts.Table) bool {
|
|
tx, ok1 := x.(*ts.MockTable)
|
|
ty, ok2 := y.(*ts.MockTable)
|
|
if !ok1 || !ok2 {
|
|
panic("table type must be MockTable")
|
|
}
|
|
return cmp.Equal(tx.State(), ty.State())
|
|
})
|
|
|
|
createDirt := func() (d ts.Dirt) {
|
|
d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"}))
|
|
d.SetTimestamp(12345)
|
|
d.Discord = 554
|
|
d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}}
|
|
d.SetWizard(map[string]*pb.Wizard{
|
|
"harry": {Stringer: pb.Stringer{X: "potter"}},
|
|
"albus": {Stringer: pb.Stringer{X: "dumbledore"}},
|
|
})
|
|
d.SetLastTime(54321)
|
|
return d
|
|
}
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: createDirt(),
|
|
y: createDirt(),
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: createDirt(),
|
|
y: createDirt(),
|
|
opts: []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: createDirt(),
|
|
y: createDirt(),
|
|
opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
|
|
}, {
|
|
label: label,
|
|
x: func() ts.Dirt {
|
|
d := createDirt()
|
|
d.SetTable(ts.CreateMockTable([]string{"a", "c"}))
|
|
d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}}
|
|
return d
|
|
}(),
|
|
y: func() ts.Dirt {
|
|
d := createDirt()
|
|
d.Discord = 500
|
|
d.SetWizard(map[string]*pb.Wizard{
|
|
"harry": {Stringer: pb.Stringer{X: "otter"}},
|
|
})
|
|
return d
|
|
}(),
|
|
opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
|
|
wantDiff: `
|
|
teststructs.Dirt{
|
|
- table: &teststructs.MockTable{state: []string{"a", "c"}},
|
|
+ table: &teststructs.MockTable{state: []string{"a", "b", "c"}},
|
|
ts: 12345,
|
|
- Discord: 554,
|
|
+ Discord: 500,
|
|
- Proto: testprotos.Dirt(Inverse(λ, s"blah")),
|
|
+ Proto: testprotos.Dirt(Inverse(λ, s"proto")),
|
|
wizard: map[string]*testprotos.Wizard{
|
|
- "albus": s"dumbledore",
|
|
- "harry": s"potter",
|
|
+ "harry": s"otter",
|
|
},
|
|
sadistic: nil,
|
|
lastTime: 54321,
|
|
... // 1 ignored field
|
|
}
|
|
`,
|
|
}}
|
|
}
|
|
|
|
func project4Tests() []test {
|
|
const label = "Project4"
|
|
|
|
allowVisibility := cmp.AllowUnexported(
|
|
ts.Cartel{},
|
|
ts.Headquarter{},
|
|
ts.Poison{},
|
|
)
|
|
|
|
transformProtos := cmp.Transformer("λ", func(x pb.Restrictions) *pb.Restrictions {
|
|
return &x
|
|
})
|
|
|
|
createCartel := func() ts.Cartel {
|
|
var p ts.Poison
|
|
p.SetPoisonType(5)
|
|
p.SetExpiration(now)
|
|
p.SetManufacturer("acme")
|
|
|
|
var hq ts.Headquarter
|
|
hq.SetID(5)
|
|
hq.SetLocation("moon")
|
|
hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"})
|
|
hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}})
|
|
hq.SetPublicMessage([]byte{1, 2, 3, 4, 5})
|
|
hq.SetHorseBack("abcdef")
|
|
hq.SetStatus(44)
|
|
|
|
var c ts.Cartel
|
|
c.Headquarter = hq
|
|
c.SetSource("mars")
|
|
c.SetCreationTime(now)
|
|
c.SetBoss("al capone")
|
|
c.SetPoisons([]*ts.Poison{&p})
|
|
|
|
return c
|
|
}
|
|
|
|
return []test{{
|
|
label: label,
|
|
x: createCartel(),
|
|
y: createCartel(),
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: createCartel(),
|
|
y: createCartel(),
|
|
opts: []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)},
|
|
wantPanic: "cannot handle unexported field",
|
|
}, {
|
|
label: label,
|
|
x: createCartel(),
|
|
y: createCartel(),
|
|
opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
|
|
}, {
|
|
label: label,
|
|
x: func() ts.Cartel {
|
|
d := createCartel()
|
|
var p1, p2 ts.Poison
|
|
p1.SetPoisonType(1)
|
|
p1.SetExpiration(now)
|
|
p1.SetManufacturer("acme")
|
|
p2.SetPoisonType(2)
|
|
p2.SetManufacturer("acme2")
|
|
d.SetPoisons([]*ts.Poison{&p1, &p2})
|
|
return d
|
|
}(),
|
|
y: func() ts.Cartel {
|
|
d := createCartel()
|
|
d.SetSubDivisions([]string{"bravo", "charlie"})
|
|
d.SetPublicMessage([]byte{1, 2, 4, 3, 5})
|
|
return d
|
|
}(),
|
|
opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
|
|
wantDiff: `
|
|
teststructs.Cartel{
|
|
Headquarter: teststructs.Headquarter{
|
|
id: 0x05,
|
|
location: "moon",
|
|
subDivisions: []string{
|
|
- "alpha",
|
|
"bravo",
|
|
"charlie",
|
|
},
|
|
incorporatedDate: s"0001-01-01 00:00:00 +0000 UTC",
|
|
metaData: s"metadata",
|
|
privateMessage: nil,
|
|
publicMessage: []uint8{
|
|
0x01,
|
|
0x02,
|
|
- 0x03,
|
|
+ 0x04,
|
|
- 0x04,
|
|
+ 0x03,
|
|
0x05,
|
|
},
|
|
horseBack: "abcdef",
|
|
rattle: "",
|
|
... // 5 identical fields
|
|
},
|
|
source: "mars",
|
|
creationDate: s"0001-01-01 00:00:00 +0000 UTC",
|
|
boss: "al capone",
|
|
lastCrimeDate: s"0001-01-01 00:00:00 +0000 UTC",
|
|
poisons: []*teststructs.Poison{
|
|
&{
|
|
- poisonType: 1,
|
|
+ poisonType: 5,
|
|
expiration: s"2009-11-10 23:00:00 +0000 UTC",
|
|
manufacturer: "acme",
|
|
potency: 0,
|
|
},
|
|
- &{poisonType: 2, manufacturer: "acme2"},
|
|
},
|
|
}
|
|
`,
|
|
}}
|
|
}
|
|
|
|
// BenchmarkBytes benchmarks the performance of performing Equal or Diff on
|
|
// large slices of bytes.
|
|
func BenchmarkBytes(b *testing.B) {
|
|
// Create a list of PathFilters that never apply, but are evaluated.
|
|
const maxFilters = 5
|
|
var filters cmp.Options
|
|
errorIface := reflect.TypeOf((*error)(nil)).Elem()
|
|
for i := 0; i <= maxFilters; i++ {
|
|
filters = append(filters, cmp.FilterPath(func(p cmp.Path) bool {
|
|
return p.Last().Type().AssignableTo(errorIface) // Never true
|
|
}, cmp.Ignore()))
|
|
}
|
|
|
|
type benchSize struct {
|
|
label string
|
|
size int64
|
|
}
|
|
for _, ts := range []benchSize{
|
|
{"4KiB", 1 << 12},
|
|
{"64KiB", 1 << 16},
|
|
{"1MiB", 1 << 20},
|
|
{"16MiB", 1 << 24},
|
|
} {
|
|
bx := append(append(make([]byte, ts.size/2), 'x'), make([]byte, ts.size/2)...)
|
|
by := append(append(make([]byte, ts.size/2), 'y'), make([]byte, ts.size/2)...)
|
|
b.Run(ts.label, func(b *testing.B) {
|
|
// Iteratively add more filters that never apply, but are evaluated
|
|
// to measure the cost of simply evaluating each filter.
|
|
for i := 0; i <= maxFilters; i++ {
|
|
b.Run(fmt.Sprintf("EqualFilter%d", i), func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
b.SetBytes(2 * ts.size)
|
|
for j := 0; j < b.N; j++ {
|
|
cmp.Equal(bx, by, filters[:i]...)
|
|
}
|
|
})
|
|
}
|
|
for i := 0; i <= maxFilters; i++ {
|
|
b.Run(fmt.Sprintf("DiffFilter%d", i), func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
b.SetBytes(2 * ts.size)
|
|
for j := 0; j < b.N; j++ {
|
|
cmp.Diff(bx, by, filters[:i]...)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|