| // 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" |
| pb "github.com/google/go-cmp/cmp/internal/testprotos" |
| ts "github.com/google/go-cmp/cmp/internal/teststructs" |
| ) |
| |
| var now = time.Now() |
| |
| func intPtr(n int) *int { return &n } |
| |
| type test struct { |
| label string // Test description |
| 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 |
| } |
| |
| 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 |
| tRunParallel(t, tt.label, func(t *testing.T) { |
| 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...) |
| }() |
| if tt.wantPanic == "" { |
| if gotPanic != "" { |
| t.Fatalf("unexpected panic message: %s", gotPanic) |
| } |
| if got, want := strings.TrimSpace(gotDiff), strings.TrimSpace(tt.wantDiff); got != want { |
| t.Fatalf("difference message:\ngot:\n%s\n\nwant:\n%s", got, want) |
| } |
| } else { |
| if !strings.Contains(gotPanic, tt.wantPanic) { |
| t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic) |
| } |
| } |
| }) |
| } |
| } |
| |
| 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: 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: "root.C:\n\t-: 3\n\t+: 4\n", |
| }, { |
| 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: "*root.A:\n\t-: 4\n\t+: 5\n", |
| }, { |
| 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: "root.R:\n\t-: s\"\"\n\t+: <nil>\n", |
| }, { |
| 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}[1]: |
| -: 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: ` |
| root: |
| -: s"hello" |
| +: s"hello2"`, |
| }, { |
| label: label, |
| x: md5.Sum([]byte{'a'}), |
| y: md5.Sum([]byte{'b'}), |
| wantDiff: ` |
| {[16]uint8}: |
| -: [16]uint8{0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61} |
| +: [16]uint8{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: ` |
| : |
| -: &<nil> |
| +: <non-existent>`, |
| }, { |
| label: label, |
| x: makeTarHeaders('0'), |
| y: makeTarHeaders('\x00'), |
| wantDiff: ` |
| {[]cmp_test.tarHeader}[0].Typeflag: |
| -: 0x30 |
| +: 0x00 |
| {[]cmp_test.tarHeader}[1].Typeflag: |
| -: 0x30 |
| +: 0x00 |
| {[]cmp_test.tarHeader}[2].Typeflag: |
| -: 0x30 |
| +: 0x00 |
| {[]cmp_test.tarHeader}[3].Typeflag: |
| -: 0x30 |
| +: 0x00 |
| {[]cmp_test.tarHeader}[4].Typeflag: |
| -: 0x30 |
| +: 0x00`, |
| }, { |
| 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}: |
| -: []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
| +: []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}`, |
| }, { |
| // 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}: |
| -: map[*testprotos.Stringer]*testprotos.Stringer{s"hello": s"world"} |
| +: map[*testprotos.Stringer]*testprotos.Stringer(nil)`, |
| }, { |
| // Ensure Stringer avoids double-quote escaping if possible. |
| label: label, |
| x: []*pb.Stringer{{`multi\nline\nline\nline`}}, |
| wantDiff: ":\n\t-: []*testprotos.Stringer{s`multi\\nline\\nline\\nline`}\n\t+: <non-existent>", |
| }, { |
| 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()), |
| }, |
| }} |
| } |
| |
| 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}))): |
| -: 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}[1]): |
| -: -5 |
| +: 3 |
| λ({[]int}[3]): |
| -: -1 |
| +: -5`, |
| }, { |
| label: label, |
| x: 0, |
| y: 1, |
| opts: []cmp.Option{ |
| cmp.Transformer("", func(in int) interface{} { |
| if in == 0 { |
| return "string" |
| } |
| return float64(in) |
| }), |
| }, |
| wantDiff: ` |
| λ({int}): |
| -: "string" |
| +: 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: ` |
| ParseJSON({string})["address"]["city"]: |
| -: "Los Angeles" |
| +: "New York" |
| ParseJSON({string})["address"]["state"]: |
| -: "CA" |
| +: "NY" |
| ParseJSON({string})["phoneNumbers"][0]["number"]: |
| -: "212 555-4321" |
| +: "212 555-1234" |
| ParseJSON({string})["spouse"]: |
| -: <non-existent> |
| +: interface {}(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: ` |
| SplitString({cmp_test.StringBytes}.String)[2]: |
| -: "Line" |
| +: "line" |
| SplitBytes({cmp_test.StringBytes}.Bytes)[3][0]: |
| -: 0x62 |
| +: 0x42`, |
| }} |
| } |
| |
| 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 |
| } |
| |
| 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.Public: |
| -: 1 |
| +: 2 |
| {teststructs.ParentStructA}.privateStruct.private: |
| -: 2 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {teststructs.ParentStructB}.PublicStruct.private: |
| -: 2 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {teststructs.ParentStructC}.privateStruct.private: |
| -: 2 |
| +: 3 |
| {teststructs.ParentStructC}.Public: |
| -: 3 |
| +: 4 |
| {teststructs.ParentStructC}.private: |
| -: 4 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {teststructs.ParentStructD}.PublicStruct.private: |
| -: 2 |
| +: 3 |
| {teststructs.ParentStructD}.Public: |
| -: 3 |
| +: 4 |
| {teststructs.ParentStructD}.private: |
| -: 4 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {teststructs.ParentStructE}.privateStruct.private: |
| -: 2 |
| +: 3 |
| {teststructs.ParentStructE}.PublicStruct.Public: |
| -: 3 |
| +: 4 |
| {teststructs.ParentStructE}.PublicStruct.private: |
| -: 4 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {teststructs.ParentStructF}.privateStruct.private: |
| -: 2 |
| +: 3 |
| {teststructs.ParentStructF}.PublicStruct.Public: |
| -: 3 |
| +: 4 |
| {teststructs.ParentStructF}.PublicStruct.private: |
| -: 4 |
| +: 5 |
| {teststructs.ParentStructF}.Public: |
| -: 5 |
| +: 6 |
| {teststructs.ParentStructF}.private: |
| -: 6 |
| +: 7`, |
| }, { |
| label: label + "ParentStructG", |
| x: ts.ParentStructG{}, |
| y: ts.ParentStructG{}, |
| wantPanic: "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.Public: |
| -: 1 |
| +: 2 |
| {*teststructs.ParentStructG}.privateStruct.private: |
| -: 2 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {*teststructs.ParentStructH}.PublicStruct.private: |
| -: 2 |
| +: 3`, |
| }, { |
| label: label + "ParentStructI", |
| x: ts.ParentStructI{}, |
| y: ts.ParentStructI{}, |
| wantPanic: "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.Public: |
| -: 1 |
| +: 2 |
| {*teststructs.ParentStructI}.privateStruct.private: |
| -: 2 |
| +: 3 |
| {*teststructs.ParentStructI}.PublicStruct.Public: |
| -: 3 |
| +: 4 |
| {*teststructs.ParentStructI}.PublicStruct.private: |
| -: 4 |
| +: 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.Public: |
| -: 1 |
| +: 2 |
| {*teststructs.ParentStructJ}.privateStruct.private: |
| -: 2 |
| +: 3 |
| {*teststructs.ParentStructJ}.PublicStruct.Public: |
| -: 3 |
| +: 4 |
| {*teststructs.ParentStructJ}.PublicStruct.private: |
| -: 4 |
| +: 5 |
| {*teststructs.ParentStructJ}.Public.Public: |
| -: 7 |
| +: 8 |
| {*teststructs.ParentStructJ}.Public.private: |
| -: 8 |
| +: 9 |
| {*teststructs.ParentStructJ}.private.Public: |
| -: 5 |
| +: 6 |
| {*teststructs.ParentStructJ}.private.private: |
| -: 6 |
| +: 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" |
| +: "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" |
| +: "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" |
| +: "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" |
| +: "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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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.X: |
| -: "NotEqual" |
| +: "not_equal" |
| {teststructs.StructD1}.X: |
| -: "NotEqual" |
| +: "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.X: |
| -: "NotEqual" |
| +: "not_equal" |
| {teststructs.StructE1}.X: |
| -: "NotEqual" |
| +: "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.X: |
| -: "NotEqual" |
| +: "not_equal" |
| {teststructs.StructF1}.X: |
| -: "NotEqual" |
| +: "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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", |
| }, { |
| 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}.Slaps[4].Args:\n\t-: s\"metadata\"\n\t+: s\"metadata2\"\n", |
| }, { |
| 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}.Dreamers[1].Animal[0].(teststructs.Goat).Immutable.ID: |
| -: "southbay2" |
| +: "southbay" |
| *{teststructs.Eagle}.Dreamers[1].Animal[0].(teststructs.Goat).Immutable.State: |
| -: testprotos.Goat_States(6) |
| +: testprotos.Goat_States(5) |
| {teststructs.Eagle}.Slaps[0].Immutable.MildSlap: |
| -: false |
| +: true |
| {teststructs.Eagle}.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices[1->?]: |
| -: "bar" |
| +: <non-existent> |
| {teststructs.Eagle}.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices[2->?]: |
| -: "baz" |
| +: <non-existent>`, |
| }} |
| } |
| |
| 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[18][0->?]: |
| -: s"germ2" |
| +: <non-existent> |
| {teststructs.GermBatch}.DirtyGerms[18][?->2]: |
| -: <non-existent> |
| +: s"germ2"`, |
| }, { |
| 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[17]: |
| -: <non-existent> |
| +: []*testprotos.Germ{s"germ1"} |
| Sort({teststructs.GermBatch}.DirtyGerms[18])[2->?]: |
| -: s"germ4" |
| +: <non-existent> |
| {teststructs.GermBatch}.DishMap[1]: |
| -: (*teststructs.Dish)(nil) |
| +: &teststructs.Dish{err: &errors.errorString{s: "unexpected EOF"}} |
| {teststructs.GermBatch}.GermStrain: |
| -: 421 |
| +: 22`, |
| }} |
| } |
| |
| 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"}} |
| +: &teststructs.MockTable{state: []string{"a", "b", "c"}} |
| {teststructs.Dirt}.Discord: |
| -: teststructs.DiscordState(554) |
| +: teststructs.DiscordState(500) |
| λ({teststructs.Dirt}.Proto): |
| -: s"blah" |
| +: s"proto" |
| {teststructs.Dirt}.wizard["albus"]: |
| -: s"dumbledore" |
| +: <non-existent> |
| {teststructs.Dirt}.wizard["harry"]: |
| -: s"potter" |
| +: s"otter"`, |
| }} |
| } |
| |
| 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.subDivisions[0->?]: |
| -: "alpha" |
| +: <non-existent> |
| {teststructs.Cartel}.Headquarter.publicMessage[2]: |
| -: 0x03 |
| +: 0x04 |
| {teststructs.Cartel}.Headquarter.publicMessage[3]: |
| -: 0x04 |
| +: 0x03 |
| {teststructs.Cartel}.poisons[0].poisonType: |
| -: testprotos.PoisonType(1) |
| +: testprotos.PoisonType(5) |
| {teststructs.Cartel}.poisons[1->?]: |
| -: &teststructs.Poison{poisonType: testprotos.PoisonType(2), manufacturer: "acme2"} |
| +: <non-existent>`, |
| }} |
| } |
| |
| // TODO: Delete this hack when we drop Go1.6 support. |
| func tRunParallel(t *testing.T, name string, f func(t *testing.T)) { |
| type runner interface { |
| Run(string, func(t *testing.T)) bool |
| } |
| var ti interface{} = t |
| if r, ok := ti.(runner); ok { |
| r.Run(name, func(t *testing.T) { |
| t.Parallel() |
| f(t) |
| }) |
| } else { |
| // Cannot run sub-tests in parallel in Go1.6. |
| t.Logf("Test: %s", name) |
| f(t) |
| } |
| } |