github.com/integration-system/go-cmp@v0.0.0-20190131081942-ac5582987a2f/cmp/compare_test.go (about) 1 // Copyright 2017, The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE.md file. 4 5 package cmp_test 6 7 import ( 8 "bytes" 9 "crypto/md5" 10 "encoding/json" 11 "fmt" 12 "io" 13 "math" 14 "math/rand" 15 "reflect" 16 "regexp" 17 "sort" 18 "strings" 19 "sync" 20 "testing" 21 "time" 22 23 "github.com/integration-system/go-cmp/cmp" 24 "github.com/integration-system/go-cmp/cmp/cmpopts" 25 pb "github.com/integration-system/go-cmp/cmp/internal/testprotos" 26 ts "github.com/integration-system/go-cmp/cmp/internal/teststructs" 27 ) 28 29 var now = time.Now() 30 31 func intPtr(n int) *int { return &n } 32 33 type test struct { 34 label string // Test description 35 x, y interface{} // Input values to compare 36 opts []cmp.Option // Input options 37 wantDiff string // The exact difference string 38 wantPanic string // Sub-string of an expected panic message 39 } 40 41 func TestDiff(t *testing.T) { 42 var tests []test 43 tests = append(tests, comparerTests()...) 44 tests = append(tests, transformerTests()...) 45 tests = append(tests, embeddedTests()...) 46 tests = append(tests, methodTests()...) 47 tests = append(tests, project1Tests()...) 48 tests = append(tests, project2Tests()...) 49 tests = append(tests, project3Tests()...) 50 tests = append(tests, project4Tests()...) 51 52 for _, tt := range tests { 53 tt := tt 54 t.Run(tt.label, func(t *testing.T) { 55 t.Parallel() 56 var gotDiff, gotPanic string 57 func() { 58 defer func() { 59 if ex := recover(); ex != nil { 60 if s, ok := ex.(string); ok { 61 gotPanic = s 62 } else { 63 panic(ex) 64 } 65 } 66 }() 67 gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...) 68 }() 69 if tt.wantPanic == "" { 70 if gotPanic != "" { 71 t.Fatalf("unexpected panic message: %s", gotPanic) 72 } 73 if got, want := strings.TrimSpace(gotDiff), strings.TrimSpace(tt.wantDiff); got != want { 74 t.Fatalf("difference message:\ngot:\n%s\n\nwant:\n%s", got, want) 75 } 76 } else { 77 if !strings.Contains(gotPanic, tt.wantPanic) { 78 t.Fatalf("panic message:\ngot: %s\nwant: %s", gotPanic, tt.wantPanic) 79 } 80 } 81 }) 82 } 83 } 84 85 func comparerTests() []test { 86 const label = "Comparer" 87 88 type Iface1 interface { 89 Method() 90 } 91 type Iface2 interface { 92 Method() 93 } 94 95 type tarHeader struct { 96 Name string 97 Mode int64 98 Uid int 99 Gid int 100 Size int64 101 ModTime time.Time 102 Typeflag byte 103 Linkname string 104 Uname string 105 Gname string 106 Devmajor int64 107 Devminor int64 108 AccessTime time.Time 109 ChangeTime time.Time 110 Xattrs map[string]string 111 } 112 113 makeTarHeaders := func(tf byte) (hs []tarHeader) { 114 for i := 0; i < 5; i++ { 115 hs = append(hs, tarHeader{ 116 Name: fmt.Sprintf("some/dummy/test/file%d", i), 117 Mode: 0664, Uid: i * 1000, Gid: i * 1000, Size: 1 << uint(i), 118 ModTime: now.Add(time.Duration(i) * time.Hour), 119 Uname: "user", Gname: "group", 120 Typeflag: tf, 121 }) 122 } 123 return hs 124 } 125 126 return []test{{ 127 label: label, 128 x: 1, 129 y: 1, 130 }, { 131 label: label, 132 x: 1, 133 y: 1, 134 opts: []cmp.Option{cmp.Ignore()}, 135 wantPanic: "cannot use an unfiltered option", 136 }, { 137 label: label, 138 x: 1, 139 y: 1, 140 opts: []cmp.Option{cmp.Comparer(func(_, _ interface{}) bool { return true })}, 141 wantPanic: "cannot use an unfiltered option", 142 }, { 143 label: label, 144 x: 1, 145 y: 1, 146 opts: []cmp.Option{cmp.Transformer("", func(x interface{}) interface{} { return x })}, 147 wantPanic: "cannot use an unfiltered option", 148 }, { 149 label: label, 150 x: 1, 151 y: 1, 152 opts: []cmp.Option{ 153 cmp.Comparer(func(x, y int) bool { return true }), 154 cmp.Transformer("", func(x int) float64 { return float64(x) }), 155 }, 156 wantPanic: "ambiguous set of applicable options", 157 }, { 158 label: label, 159 x: 1, 160 y: 1, 161 opts: []cmp.Option{ 162 cmp.FilterPath(func(p cmp.Path) bool { 163 return len(p) > 0 && p[len(p)-1].Type().Kind() == reflect.Int 164 }, cmp.Options{cmp.Ignore(), cmp.Ignore(), cmp.Ignore()}), 165 cmp.Comparer(func(x, y int) bool { return true }), 166 cmp.Transformer("", func(x int) float64 { return float64(x) }), 167 }, 168 }, { 169 label: label, 170 opts: []cmp.Option{struct{ cmp.Option }{}}, 171 wantPanic: "unknown option", 172 }, { 173 label: label, 174 x: struct{ A, B, C int }{1, 2, 3}, 175 y: struct{ A, B, C int }{1, 2, 3}, 176 }, { 177 label: label, 178 x: struct{ A, B, C int }{1, 2, 3}, 179 y: struct{ A, B, C int }{1, 2, 4}, 180 wantDiff: "root.C:\n\t-: 3\n\t+: 4\n", 181 }, { 182 label: label, 183 x: struct{ a, b, c int }{1, 2, 3}, 184 y: struct{ a, b, c int }{1, 2, 4}, 185 wantPanic: "cannot handle unexported field", 186 }, { 187 label: label, 188 x: &struct{ A *int }{intPtr(4)}, 189 y: &struct{ A *int }{intPtr(4)}, 190 }, { 191 label: label, 192 x: &struct{ A *int }{intPtr(4)}, 193 y: &struct{ A *int }{intPtr(5)}, 194 wantDiff: "*root.A:\n\t-: 4\n\t+: 5\n", 195 }, { 196 label: label, 197 x: &struct{ A *int }{intPtr(4)}, 198 y: &struct{ A *int }{intPtr(5)}, 199 opts: []cmp.Option{ 200 cmp.Comparer(func(x, y int) bool { return true }), 201 }, 202 }, { 203 label: label, 204 x: &struct{ A *int }{intPtr(4)}, 205 y: &struct{ A *int }{intPtr(5)}, 206 opts: []cmp.Option{ 207 cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }), 208 }, 209 }, { 210 label: label, 211 x: &struct{ R *bytes.Buffer }{}, 212 y: &struct{ R *bytes.Buffer }{}, 213 }, { 214 label: label, 215 x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)}, 216 y: &struct{ R *bytes.Buffer }{}, 217 wantDiff: "root.R:\n\t-: s\"\"\n\t+: <nil>\n", 218 }, { 219 label: label, 220 x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)}, 221 y: &struct{ R *bytes.Buffer }{}, 222 opts: []cmp.Option{ 223 cmp.Comparer(func(x, y io.Reader) bool { return true }), 224 }, 225 }, { 226 label: label, 227 x: &struct{ R bytes.Buffer }{}, 228 y: &struct{ R bytes.Buffer }{}, 229 wantPanic: "cannot handle unexported field", 230 }, { 231 label: label, 232 x: &struct{ R bytes.Buffer }{}, 233 y: &struct{ R bytes.Buffer }{}, 234 opts: []cmp.Option{ 235 cmp.Comparer(func(x, y io.Reader) bool { return true }), 236 }, 237 wantPanic: "cannot handle unexported field", 238 }, { 239 label: label, 240 x: &struct{ R bytes.Buffer }{}, 241 y: &struct{ R bytes.Buffer }{}, 242 opts: []cmp.Option{ 243 cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }), 244 cmp.Comparer(func(x, y io.Reader) bool { return true }), 245 }, 246 }, { 247 label: label, 248 x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, 249 y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, 250 wantPanic: "cannot handle unexported field", 251 }, { 252 label: label, 253 x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, 254 y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, 255 opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool { 256 if x == nil || y == nil { 257 return x == nil && y == nil 258 } 259 return x.String() == y.String() 260 })}, 261 }, { 262 label: label, 263 x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")}, 264 y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*d*")}, 265 opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool { 266 if x == nil || y == nil { 267 return x == nil && y == nil 268 } 269 return x.String() == y.String() 270 })}, 271 wantDiff: ` 272 {[]*regexp.Regexp}[1]: 273 -: s"a*b*c*" 274 +: s"a*b*d*"`, 275 }, { 276 label: label, 277 x: func() ***int { 278 a := 0 279 b := &a 280 c := &b 281 return &c 282 }(), 283 y: func() ***int { 284 a := 0 285 b := &a 286 c := &b 287 return &c 288 }(), 289 }, { 290 label: label, 291 x: func() ***int { 292 a := 0 293 b := &a 294 c := &b 295 return &c 296 }(), 297 y: func() ***int { 298 a := 1 299 b := &a 300 c := &b 301 return &c 302 }(), 303 wantDiff: ` 304 ***{***int}: 305 -: 0 306 +: 1`, 307 }, { 308 label: label, 309 x: []int{1, 2, 3, 4, 5}[:3], 310 y: []int{1, 2, 3}, 311 }, { 312 label: label, 313 x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")}, 314 y: struct{ fmt.Stringer }{regexp.MustCompile("hello")}, 315 opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, 316 }, { 317 label: label, 318 x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")}, 319 y: struct{ fmt.Stringer }{regexp.MustCompile("hello2")}, 320 opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })}, 321 wantDiff: ` 322 root: 323 -: s"hello" 324 +: s"hello2"`, 325 }, { 326 label: label, 327 x: md5.Sum([]byte{'a'}), 328 y: md5.Sum([]byte{'b'}), 329 wantDiff: ` 330 {[16]uint8}: 331 -: [16]uint8{0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61} 332 +: [16]uint8{0x92, 0xeb, 0x5f, 0xfe, 0xe6, 0xae, 0x2f, 0xec, 0x3a, 0xd7, 0x1c, 0x77, 0x75, 0x31, 0x57, 0x8f}`, 333 }, { 334 label: label, 335 x: new(fmt.Stringer), 336 y: nil, 337 wantDiff: ` 338 : 339 -: &<nil> 340 +: <non-existent>`, 341 }, { 342 label: label, 343 x: makeTarHeaders('0'), 344 y: makeTarHeaders('\x00'), 345 wantDiff: ` 346 {[]cmp_test.tarHeader}[0].Typeflag: 347 -: 0x30 348 +: 0x00 349 {[]cmp_test.tarHeader}[1].Typeflag: 350 -: 0x30 351 +: 0x00 352 {[]cmp_test.tarHeader}[2].Typeflag: 353 -: 0x30 354 +: 0x00 355 {[]cmp_test.tarHeader}[3].Typeflag: 356 -: 0x30 357 +: 0x00 358 {[]cmp_test.tarHeader}[4].Typeflag: 359 -: 0x30 360 +: 0x00`, 361 }, { 362 label: label, 363 x: make([]int, 1000), 364 y: make([]int, 1000), 365 opts: []cmp.Option{ 366 cmp.Comparer(func(_, _ int) bool { 367 return rand.Intn(2) == 0 368 }), 369 }, 370 wantPanic: "non-deterministic or non-symmetric function detected", 371 }, { 372 label: label, 373 x: make([]int, 1000), 374 y: make([]int, 1000), 375 opts: []cmp.Option{ 376 cmp.FilterValues(func(_, _ int) bool { 377 return rand.Intn(2) == 0 378 }, cmp.Ignore()), 379 }, 380 wantPanic: "non-deterministic or non-symmetric function detected", 381 }, { 382 label: label, 383 x: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 384 y: []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, 385 opts: []cmp.Option{ 386 cmp.Comparer(func(x, y int) bool { 387 return x < y 388 }), 389 }, 390 wantPanic: "non-deterministic or non-symmetric function detected", 391 }, { 392 label: label, 393 x: make([]string, 1000), 394 y: make([]string, 1000), 395 opts: []cmp.Option{ 396 cmp.Transformer("", func(x string) int { 397 return rand.Int() 398 }), 399 }, 400 wantPanic: "non-deterministic function detected", 401 }, { 402 // Make sure the dynamic checks don't raise a false positive for 403 // non-reflexive comparisons. 404 label: label, 405 x: make([]int, 10), 406 y: make([]int, 10), 407 opts: []cmp.Option{ 408 cmp.Transformer("", func(x int) float64 { 409 return math.NaN() 410 }), 411 }, 412 wantDiff: ` 413 {[]int}: 414 -: []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 415 +: []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}`, 416 }, { 417 // Ensure reasonable Stringer formatting of map keys. 418 label: label, 419 x: map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}}, 420 y: map[*pb.Stringer]*pb.Stringer(nil), 421 wantDiff: ` 422 {map[*testprotos.Stringer]*testprotos.Stringer}: 423 -: map[*testprotos.Stringer]*testprotos.Stringer{s"hello": s"world"} 424 +: map[*testprotos.Stringer]*testprotos.Stringer(nil)`, 425 }, { 426 // Ensure Stringer avoids double-quote escaping if possible. 427 label: label, 428 x: []*pb.Stringer{{`multi\nline\nline\nline`}}, 429 wantDiff: ":\n\t-: []*testprotos.Stringer{s`multi\\nline\\nline\\nline`}\n\t+: <non-existent>", 430 }, { 431 label: label, 432 x: struct{ I Iface2 }{}, 433 y: struct{ I Iface2 }{}, 434 opts: []cmp.Option{ 435 cmp.Comparer(func(x, y Iface1) bool { 436 return x == nil && y == nil 437 }), 438 }, 439 }, { 440 label: label, 441 x: struct{ I Iface2 }{}, 442 y: struct{ I Iface2 }{}, 443 opts: []cmp.Option{ 444 cmp.Transformer("", func(v Iface1) bool { 445 return v == nil 446 }), 447 }, 448 }, { 449 label: label, 450 x: struct{ I Iface2 }{}, 451 y: struct{ I Iface2 }{}, 452 opts: []cmp.Option{ 453 cmp.FilterValues(func(x, y Iface1) bool { 454 return x == nil && y == nil 455 }, cmp.Ignore()), 456 }, 457 }, { 458 label: label, 459 x: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}}, 460 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"}}, 461 wantDiff: ` 462 root[0]["hr"]: 463 -: int(65) 464 +: float64(65) 465 root[1]["hr"]: 466 -: int(63) 467 +: float64(63)`, 468 }} 469 } 470 471 func transformerTests() []test { 472 type StringBytes struct { 473 String string 474 Bytes []byte 475 } 476 477 const label = "Transformer" 478 479 transformOnce := func(name string, f interface{}) cmp.Option { 480 xform := cmp.Transformer(name, f) 481 return cmp.FilterPath(func(p cmp.Path) bool { 482 for _, ps := range p { 483 if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform { 484 return false 485 } 486 } 487 return true 488 }, xform) 489 } 490 491 return []test{{ 492 label: label, 493 x: uint8(0), 494 y: uint8(1), 495 opts: []cmp.Option{ 496 cmp.Transformer("", func(in uint8) uint16 { return uint16(in) }), 497 cmp.Transformer("", func(in uint16) uint32 { return uint32(in) }), 498 cmp.Transformer("", func(in uint32) uint64 { return uint64(in) }), 499 }, 500 wantDiff: ` 501 λ(λ(λ({uint8}))): 502 -: 0x00 503 +: 0x01`, 504 }, { 505 label: label, 506 x: 0, 507 y: 1, 508 opts: []cmp.Option{ 509 cmp.Transformer("", func(in int) int { return in / 2 }), 510 cmp.Transformer("", func(in int) int { return in }), 511 }, 512 wantPanic: "ambiguous set of applicable options", 513 }, { 514 label: label, 515 x: []int{0, -5, 0, -1}, 516 y: []int{1, 3, 0, -5}, 517 opts: []cmp.Option{ 518 cmp.FilterValues( 519 func(x, y int) bool { return x+y >= 0 }, 520 cmp.Transformer("", func(in int) int64 { return int64(in / 2) }), 521 ), 522 cmp.FilterValues( 523 func(x, y int) bool { return x+y < 0 }, 524 cmp.Transformer("", func(in int) int64 { return int64(in) }), 525 ), 526 }, 527 wantDiff: ` 528 λ({[]int}[1]): 529 -: -5 530 +: 3 531 λ({[]int}[3]): 532 -: -1 533 +: -5`, 534 }, { 535 label: label, 536 x: 0, 537 y: 1, 538 opts: []cmp.Option{ 539 cmp.Transformer("", func(in int) interface{} { 540 if in == 0 { 541 return "string" 542 } 543 return float64(in) 544 }), 545 }, 546 wantDiff: ` 547 λ({int}): 548 -: "string" 549 +: 1`, 550 }, { 551 label: label, 552 x: `{ 553 "firstName": "John", 554 "lastName": "Smith", 555 "age": 25, 556 "isAlive": true, 557 "address": { 558 "city": "Los Angeles", 559 "postalCode": "10021-3100", 560 "state": "CA", 561 "streetAddress": "21 2nd Street" 562 }, 563 "phoneNumbers": [{ 564 "type": "home", 565 "number": "212 555-4321" 566 },{ 567 "type": "office", 568 "number": "646 555-4567" 569 },{ 570 "number": "123 456-7890", 571 "type": "mobile" 572 }], 573 "children": [] 574 }`, 575 y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25, 576 "address":{"streetAddress":"21 2nd Street","city":"New York", 577 "state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home", 578 "number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{ 579 "type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`, 580 opts: []cmp.Option{ 581 transformOnce("ParseJSON", func(s string) (m map[string]interface{}) { 582 if err := json.Unmarshal([]byte(s), &m); err != nil { 583 panic(err) 584 } 585 return m 586 }), 587 }, 588 wantDiff: ` 589 ParseJSON({string})["address"]["city"]: 590 -: "Los Angeles" 591 +: "New York" 592 ParseJSON({string})["address"]["state"]: 593 -: "CA" 594 +: "NY" 595 ParseJSON({string})["phoneNumbers"][0]["number"]: 596 -: "212 555-4321" 597 +: "212 555-1234" 598 ParseJSON({string})["spouse"]: 599 -: <non-existent> 600 +: interface {}(nil)`, 601 }, { 602 label: label, 603 x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")}, 604 y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")}, 605 opts: []cmp.Option{ 606 transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }), 607 transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }), 608 }, 609 wantDiff: ` 610 SplitString({cmp_test.StringBytes}.String)[2]: 611 -: "Line" 612 +: "line" 613 SplitBytes({cmp_test.StringBytes}.Bytes)[3][0]: 614 -: 0x62 615 +: 0x42`, 616 }, { 617 x: "a\nb\nc\n", 618 y: "a\nb\nc\n", 619 opts: []cmp.Option{ 620 cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }), 621 }, 622 wantPanic: "recursive set of Transformers detected", 623 }, { 624 x: complex64(0), 625 y: complex64(0), 626 opts: []cmp.Option{ 627 cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }), 628 cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }), 629 cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }), 630 }, 631 wantPanic: "recursive set of Transformers detected", 632 }} 633 } 634 635 func embeddedTests() []test { 636 const label = "EmbeddedStruct/" 637 638 privateStruct := *new(ts.ParentStructA).PrivateStruct() 639 640 createStructA := func(i int) ts.ParentStructA { 641 s := ts.ParentStructA{} 642 s.PrivateStruct().Public = 1 + i 643 s.PrivateStruct().SetPrivate(2 + i) 644 return s 645 } 646 647 createStructB := func(i int) ts.ParentStructB { 648 s := ts.ParentStructB{} 649 s.PublicStruct.Public = 1 + i 650 s.PublicStruct.SetPrivate(2 + i) 651 return s 652 } 653 654 createStructC := func(i int) ts.ParentStructC { 655 s := ts.ParentStructC{} 656 s.PrivateStruct().Public = 1 + i 657 s.PrivateStruct().SetPrivate(2 + i) 658 s.Public = 3 + i 659 s.SetPrivate(4 + i) 660 return s 661 } 662 663 createStructD := func(i int) ts.ParentStructD { 664 s := ts.ParentStructD{} 665 s.PublicStruct.Public = 1 + i 666 s.PublicStruct.SetPrivate(2 + i) 667 s.Public = 3 + i 668 s.SetPrivate(4 + i) 669 return s 670 } 671 672 createStructE := func(i int) ts.ParentStructE { 673 s := ts.ParentStructE{} 674 s.PrivateStruct().Public = 1 + i 675 s.PrivateStruct().SetPrivate(2 + i) 676 s.PublicStruct.Public = 3 + i 677 s.PublicStruct.SetPrivate(4 + i) 678 return s 679 } 680 681 createStructF := func(i int) ts.ParentStructF { 682 s := ts.ParentStructF{} 683 s.PrivateStruct().Public = 1 + i 684 s.PrivateStruct().SetPrivate(2 + i) 685 s.PublicStruct.Public = 3 + i 686 s.PublicStruct.SetPrivate(4 + i) 687 s.Public = 5 + i 688 s.SetPrivate(6 + i) 689 return s 690 } 691 692 createStructG := func(i int) *ts.ParentStructG { 693 s := ts.NewParentStructG() 694 s.PrivateStruct().Public = 1 + i 695 s.PrivateStruct().SetPrivate(2 + i) 696 return s 697 } 698 699 createStructH := func(i int) *ts.ParentStructH { 700 s := ts.NewParentStructH() 701 s.PublicStruct.Public = 1 + i 702 s.PublicStruct.SetPrivate(2 + i) 703 return s 704 } 705 706 createStructI := func(i int) *ts.ParentStructI { 707 s := ts.NewParentStructI() 708 s.PrivateStruct().Public = 1 + i 709 s.PrivateStruct().SetPrivate(2 + i) 710 s.PublicStruct.Public = 3 + i 711 s.PublicStruct.SetPrivate(4 + i) 712 return s 713 } 714 715 createStructJ := func(i int) *ts.ParentStructJ { 716 s := ts.NewParentStructJ() 717 s.PrivateStruct().Public = 1 + i 718 s.PrivateStruct().SetPrivate(2 + i) 719 s.PublicStruct.Public = 3 + i 720 s.PublicStruct.SetPrivate(4 + i) 721 s.Private().Public = 5 + i 722 s.Private().SetPrivate(6 + i) 723 s.Public.Public = 7 + i 724 s.Public.SetPrivate(8 + i) 725 return s 726 } 727 728 return []test{{ 729 label: label + "ParentStructA", 730 x: ts.ParentStructA{}, 731 y: ts.ParentStructA{}, 732 wantPanic: "cannot handle unexported field", 733 }, { 734 label: label + "ParentStructA", 735 x: ts.ParentStructA{}, 736 y: ts.ParentStructA{}, 737 opts: []cmp.Option{ 738 cmpopts.IgnoreUnexported(ts.ParentStructA{}), 739 }, 740 }, { 741 label: label + "ParentStructA", 742 x: createStructA(0), 743 y: createStructA(0), 744 opts: []cmp.Option{ 745 cmp.AllowUnexported(ts.ParentStructA{}), 746 }, 747 wantPanic: "cannot handle unexported field", 748 }, { 749 label: label + "ParentStructA", 750 x: createStructA(0), 751 y: createStructA(0), 752 opts: []cmp.Option{ 753 cmp.AllowUnexported(ts.ParentStructA{}, privateStruct), 754 }, 755 }, { 756 label: label + "ParentStructA", 757 x: createStructA(0), 758 y: createStructA(1), 759 opts: []cmp.Option{ 760 cmp.AllowUnexported(ts.ParentStructA{}, privateStruct), 761 }, 762 wantDiff: ` 763 {teststructs.ParentStructA}.privateStruct.Public: 764 -: 1 765 +: 2 766 {teststructs.ParentStructA}.privateStruct.private: 767 -: 2 768 +: 3`, 769 }, { 770 label: label + "ParentStructB", 771 x: ts.ParentStructB{}, 772 y: ts.ParentStructB{}, 773 opts: []cmp.Option{ 774 cmpopts.IgnoreUnexported(ts.ParentStructB{}), 775 }, 776 wantPanic: "cannot handle unexported field", 777 }, { 778 label: label + "ParentStructB", 779 x: ts.ParentStructB{}, 780 y: ts.ParentStructB{}, 781 opts: []cmp.Option{ 782 cmpopts.IgnoreUnexported(ts.ParentStructB{}), 783 cmpopts.IgnoreUnexported(ts.PublicStruct{}), 784 }, 785 }, { 786 label: label + "ParentStructB", 787 x: createStructB(0), 788 y: createStructB(0), 789 opts: []cmp.Option{ 790 cmp.AllowUnexported(ts.ParentStructB{}), 791 }, 792 wantPanic: "cannot handle unexported field", 793 }, { 794 label: label + "ParentStructB", 795 x: createStructB(0), 796 y: createStructB(0), 797 opts: []cmp.Option{ 798 cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}), 799 }, 800 }, { 801 label: label + "ParentStructB", 802 x: createStructB(0), 803 y: createStructB(1), 804 opts: []cmp.Option{ 805 cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}), 806 }, 807 wantDiff: ` 808 {teststructs.ParentStructB}.PublicStruct.Public: 809 -: 1 810 +: 2 811 {teststructs.ParentStructB}.PublicStruct.private: 812 -: 2 813 +: 3`, 814 }, { 815 label: label + "ParentStructC", 816 x: ts.ParentStructC{}, 817 y: ts.ParentStructC{}, 818 wantPanic: "cannot handle unexported field", 819 }, { 820 label: label + "ParentStructC", 821 x: ts.ParentStructC{}, 822 y: ts.ParentStructC{}, 823 opts: []cmp.Option{ 824 cmpopts.IgnoreUnexported(ts.ParentStructC{}), 825 }, 826 }, { 827 label: label + "ParentStructC", 828 x: createStructC(0), 829 y: createStructC(0), 830 opts: []cmp.Option{ 831 cmp.AllowUnexported(ts.ParentStructC{}), 832 }, 833 wantPanic: "cannot handle unexported field", 834 }, { 835 label: label + "ParentStructC", 836 x: createStructC(0), 837 y: createStructC(0), 838 opts: []cmp.Option{ 839 cmp.AllowUnexported(ts.ParentStructC{}, privateStruct), 840 }, 841 }, { 842 label: label + "ParentStructC", 843 x: createStructC(0), 844 y: createStructC(1), 845 opts: []cmp.Option{ 846 cmp.AllowUnexported(ts.ParentStructC{}, privateStruct), 847 }, 848 wantDiff: ` 849 {teststructs.ParentStructC}.privateStruct.Public: 850 -: 1 851 +: 2 852 {teststructs.ParentStructC}.privateStruct.private: 853 -: 2 854 +: 3 855 {teststructs.ParentStructC}.Public: 856 -: 3 857 +: 4 858 {teststructs.ParentStructC}.private: 859 -: 4 860 +: 5`, 861 }, { 862 label: label + "ParentStructD", 863 x: ts.ParentStructD{}, 864 y: ts.ParentStructD{}, 865 opts: []cmp.Option{ 866 cmpopts.IgnoreUnexported(ts.ParentStructD{}), 867 }, 868 wantPanic: "cannot handle unexported field", 869 }, { 870 label: label + "ParentStructD", 871 x: ts.ParentStructD{}, 872 y: ts.ParentStructD{}, 873 opts: []cmp.Option{ 874 cmpopts.IgnoreUnexported(ts.ParentStructD{}), 875 cmpopts.IgnoreUnexported(ts.PublicStruct{}), 876 }, 877 }, { 878 label: label + "ParentStructD", 879 x: createStructD(0), 880 y: createStructD(0), 881 opts: []cmp.Option{ 882 cmp.AllowUnexported(ts.ParentStructD{}), 883 }, 884 wantPanic: "cannot handle unexported field", 885 }, { 886 label: label + "ParentStructD", 887 x: createStructD(0), 888 y: createStructD(0), 889 opts: []cmp.Option{ 890 cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}), 891 }, 892 }, { 893 label: label + "ParentStructD", 894 x: createStructD(0), 895 y: createStructD(1), 896 opts: []cmp.Option{ 897 cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}), 898 }, 899 wantDiff: ` 900 {teststructs.ParentStructD}.PublicStruct.Public: 901 -: 1 902 +: 2 903 {teststructs.ParentStructD}.PublicStruct.private: 904 -: 2 905 +: 3 906 {teststructs.ParentStructD}.Public: 907 -: 3 908 +: 4 909 {teststructs.ParentStructD}.private: 910 -: 4 911 +: 5`, 912 }, { 913 label: label + "ParentStructE", 914 x: ts.ParentStructE{}, 915 y: ts.ParentStructE{}, 916 opts: []cmp.Option{ 917 cmpopts.IgnoreUnexported(ts.ParentStructE{}), 918 }, 919 wantPanic: "cannot handle unexported field", 920 }, { 921 label: label + "ParentStructE", 922 x: ts.ParentStructE{}, 923 y: ts.ParentStructE{}, 924 opts: []cmp.Option{ 925 cmpopts.IgnoreUnexported(ts.ParentStructE{}), 926 cmpopts.IgnoreUnexported(ts.PublicStruct{}), 927 }, 928 }, { 929 label: label + "ParentStructE", 930 x: createStructE(0), 931 y: createStructE(0), 932 opts: []cmp.Option{ 933 cmp.AllowUnexported(ts.ParentStructE{}), 934 }, 935 wantPanic: "cannot handle unexported field", 936 }, { 937 label: label + "ParentStructE", 938 x: createStructE(0), 939 y: createStructE(0), 940 opts: []cmp.Option{ 941 cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}), 942 }, 943 wantPanic: "cannot handle unexported field", 944 }, { 945 label: label + "ParentStructE", 946 x: createStructE(0), 947 y: createStructE(0), 948 opts: []cmp.Option{ 949 cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct), 950 }, 951 }, { 952 label: label + "ParentStructE", 953 x: createStructE(0), 954 y: createStructE(1), 955 opts: []cmp.Option{ 956 cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct), 957 }, 958 wantDiff: ` 959 {teststructs.ParentStructE}.privateStruct.Public: 960 -: 1 961 +: 2 962 {teststructs.ParentStructE}.privateStruct.private: 963 -: 2 964 +: 3 965 {teststructs.ParentStructE}.PublicStruct.Public: 966 -: 3 967 +: 4 968 {teststructs.ParentStructE}.PublicStruct.private: 969 -: 4 970 +: 5`, 971 }, { 972 label: label + "ParentStructF", 973 x: ts.ParentStructF{}, 974 y: ts.ParentStructF{}, 975 opts: []cmp.Option{ 976 cmpopts.IgnoreUnexported(ts.ParentStructF{}), 977 }, 978 wantPanic: "cannot handle unexported field", 979 }, { 980 label: label + "ParentStructF", 981 x: ts.ParentStructF{}, 982 y: ts.ParentStructF{}, 983 opts: []cmp.Option{ 984 cmpopts.IgnoreUnexported(ts.ParentStructF{}), 985 cmpopts.IgnoreUnexported(ts.PublicStruct{}), 986 }, 987 }, { 988 label: label + "ParentStructF", 989 x: createStructF(0), 990 y: createStructF(0), 991 opts: []cmp.Option{ 992 cmp.AllowUnexported(ts.ParentStructF{}), 993 }, 994 wantPanic: "cannot handle unexported field", 995 }, { 996 label: label + "ParentStructF", 997 x: createStructF(0), 998 y: createStructF(0), 999 opts: []cmp.Option{ 1000 cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}), 1001 }, 1002 wantPanic: "cannot handle unexported field", 1003 }, { 1004 label: label + "ParentStructF", 1005 x: createStructF(0), 1006 y: createStructF(0), 1007 opts: []cmp.Option{ 1008 cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct), 1009 }, 1010 }, { 1011 label: label + "ParentStructF", 1012 x: createStructF(0), 1013 y: createStructF(1), 1014 opts: []cmp.Option{ 1015 cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct), 1016 }, 1017 wantDiff: ` 1018 {teststructs.ParentStructF}.privateStruct.Public: 1019 -: 1 1020 +: 2 1021 {teststructs.ParentStructF}.privateStruct.private: 1022 -: 2 1023 +: 3 1024 {teststructs.ParentStructF}.PublicStruct.Public: 1025 -: 3 1026 +: 4 1027 {teststructs.ParentStructF}.PublicStruct.private: 1028 -: 4 1029 +: 5 1030 {teststructs.ParentStructF}.Public: 1031 -: 5 1032 +: 6 1033 {teststructs.ParentStructF}.private: 1034 -: 6 1035 +: 7`, 1036 }, { 1037 label: label + "ParentStructG", 1038 x: ts.ParentStructG{}, 1039 y: ts.ParentStructG{}, 1040 wantPanic: "cannot handle unexported field", 1041 }, { 1042 label: label + "ParentStructG", 1043 x: ts.ParentStructG{}, 1044 y: ts.ParentStructG{}, 1045 opts: []cmp.Option{ 1046 cmpopts.IgnoreUnexported(ts.ParentStructG{}), 1047 }, 1048 }, { 1049 label: label + "ParentStructG", 1050 x: createStructG(0), 1051 y: createStructG(0), 1052 opts: []cmp.Option{ 1053 cmp.AllowUnexported(ts.ParentStructG{}), 1054 }, 1055 wantPanic: "cannot handle unexported field", 1056 }, { 1057 label: label + "ParentStructG", 1058 x: createStructG(0), 1059 y: createStructG(0), 1060 opts: []cmp.Option{ 1061 cmp.AllowUnexported(ts.ParentStructG{}, privateStruct), 1062 }, 1063 }, { 1064 label: label + "ParentStructG", 1065 x: createStructG(0), 1066 y: createStructG(1), 1067 opts: []cmp.Option{ 1068 cmp.AllowUnexported(ts.ParentStructG{}, privateStruct), 1069 }, 1070 wantDiff: ` 1071 {*teststructs.ParentStructG}.privateStruct.Public: 1072 -: 1 1073 +: 2 1074 {*teststructs.ParentStructG}.privateStruct.private: 1075 -: 2 1076 +: 3`, 1077 }, { 1078 label: label + "ParentStructH", 1079 x: ts.ParentStructH{}, 1080 y: ts.ParentStructH{}, 1081 }, { 1082 label: label + "ParentStructH", 1083 x: createStructH(0), 1084 y: createStructH(0), 1085 wantPanic: "cannot handle unexported field", 1086 }, { 1087 label: label + "ParentStructH", 1088 x: ts.ParentStructH{}, 1089 y: ts.ParentStructH{}, 1090 opts: []cmp.Option{ 1091 cmpopts.IgnoreUnexported(ts.ParentStructH{}), 1092 }, 1093 }, { 1094 label: label + "ParentStructH", 1095 x: createStructH(0), 1096 y: createStructH(0), 1097 opts: []cmp.Option{ 1098 cmp.AllowUnexported(ts.ParentStructH{}), 1099 }, 1100 wantPanic: "cannot handle unexported field", 1101 }, { 1102 label: label + "ParentStructH", 1103 x: createStructH(0), 1104 y: createStructH(0), 1105 opts: []cmp.Option{ 1106 cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}), 1107 }, 1108 }, { 1109 label: label + "ParentStructH", 1110 x: createStructH(0), 1111 y: createStructH(1), 1112 opts: []cmp.Option{ 1113 cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}), 1114 }, 1115 wantDiff: ` 1116 {*teststructs.ParentStructH}.PublicStruct.Public: 1117 -: 1 1118 +: 2 1119 {*teststructs.ParentStructH}.PublicStruct.private: 1120 -: 2 1121 +: 3`, 1122 }, { 1123 label: label + "ParentStructI", 1124 x: ts.ParentStructI{}, 1125 y: ts.ParentStructI{}, 1126 wantPanic: "cannot handle unexported field", 1127 }, { 1128 label: label + "ParentStructI", 1129 x: ts.ParentStructI{}, 1130 y: ts.ParentStructI{}, 1131 opts: []cmp.Option{ 1132 cmpopts.IgnoreUnexported(ts.ParentStructI{}), 1133 }, 1134 }, { 1135 label: label + "ParentStructI", 1136 x: createStructI(0), 1137 y: createStructI(0), 1138 opts: []cmp.Option{ 1139 cmpopts.IgnoreUnexported(ts.ParentStructI{}), 1140 }, 1141 wantPanic: "cannot handle unexported field", 1142 }, { 1143 label: label + "ParentStructI", 1144 x: createStructI(0), 1145 y: createStructI(0), 1146 opts: []cmp.Option{ 1147 cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}), 1148 }, 1149 }, { 1150 label: label + "ParentStructI", 1151 x: createStructI(0), 1152 y: createStructI(0), 1153 opts: []cmp.Option{ 1154 cmp.AllowUnexported(ts.ParentStructI{}), 1155 }, 1156 wantPanic: "cannot handle unexported field", 1157 }, { 1158 label: label + "ParentStructI", 1159 x: createStructI(0), 1160 y: createStructI(0), 1161 opts: []cmp.Option{ 1162 cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct), 1163 }, 1164 }, { 1165 label: label + "ParentStructI", 1166 x: createStructI(0), 1167 y: createStructI(1), 1168 opts: []cmp.Option{ 1169 cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct), 1170 }, 1171 wantDiff: ` 1172 {*teststructs.ParentStructI}.privateStruct.Public: 1173 -: 1 1174 +: 2 1175 {*teststructs.ParentStructI}.privateStruct.private: 1176 -: 2 1177 +: 3 1178 {*teststructs.ParentStructI}.PublicStruct.Public: 1179 -: 3 1180 +: 4 1181 {*teststructs.ParentStructI}.PublicStruct.private: 1182 -: 4 1183 +: 5`, 1184 }, { 1185 label: label + "ParentStructJ", 1186 x: ts.ParentStructJ{}, 1187 y: ts.ParentStructJ{}, 1188 wantPanic: "cannot handle unexported field", 1189 }, { 1190 label: label + "ParentStructJ", 1191 x: ts.ParentStructJ{}, 1192 y: ts.ParentStructJ{}, 1193 opts: []cmp.Option{ 1194 cmpopts.IgnoreUnexported(ts.ParentStructJ{}), 1195 }, 1196 wantPanic: "cannot handle unexported field", 1197 }, { 1198 label: label + "ParentStructJ", 1199 x: ts.ParentStructJ{}, 1200 y: ts.ParentStructJ{}, 1201 opts: []cmp.Option{ 1202 cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}), 1203 }, 1204 }, { 1205 label: label + "ParentStructJ", 1206 x: createStructJ(0), 1207 y: createStructJ(0), 1208 opts: []cmp.Option{ 1209 cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}), 1210 }, 1211 wantPanic: "cannot handle unexported field", 1212 }, { 1213 label: label + "ParentStructJ", 1214 x: createStructJ(0), 1215 y: createStructJ(0), 1216 opts: []cmp.Option{ 1217 cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct), 1218 }, 1219 }, { 1220 label: label + "ParentStructJ", 1221 x: createStructJ(0), 1222 y: createStructJ(1), 1223 opts: []cmp.Option{ 1224 cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct), 1225 }, 1226 wantDiff: ` 1227 {*teststructs.ParentStructJ}.privateStruct.Public: 1228 -: 1 1229 +: 2 1230 {*teststructs.ParentStructJ}.privateStruct.private: 1231 -: 2 1232 +: 3 1233 {*teststructs.ParentStructJ}.PublicStruct.Public: 1234 -: 3 1235 +: 4 1236 {*teststructs.ParentStructJ}.PublicStruct.private: 1237 -: 4 1238 +: 5 1239 {*teststructs.ParentStructJ}.Public.Public: 1240 -: 7 1241 +: 8 1242 {*teststructs.ParentStructJ}.Public.private: 1243 -: 8 1244 +: 9 1245 {*teststructs.ParentStructJ}.private.Public: 1246 -: 5 1247 +: 6 1248 {*teststructs.ParentStructJ}.private.private: 1249 -: 6 1250 +: 7`, 1251 }} 1252 } 1253 1254 func methodTests() []test { 1255 const label = "EqualMethod/" 1256 1257 // A common mistake that the Equal method is on a pointer receiver, 1258 // but only a non-pointer value is present in the struct. 1259 // A transform can be used to forcibly reference the value. 1260 derefTransform := cmp.FilterPath(func(p cmp.Path) bool { 1261 if len(p) == 0 { 1262 return false 1263 } 1264 t := p[len(p)-1].Type() 1265 if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr { 1266 return false 1267 } 1268 if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok { 1269 tf := m.Func.Type() 1270 return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 && 1271 tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true) 1272 } 1273 return false 1274 }, cmp.Transformer("Ref", func(x interface{}) interface{} { 1275 v := reflect.ValueOf(x) 1276 vp := reflect.New(v.Type()) 1277 vp.Elem().Set(v) 1278 return vp.Interface() 1279 })) 1280 1281 // For each of these types, there is an Equal method defined, which always 1282 // returns true, while the underlying data are fundamentally different. 1283 // Since the method should be called, these are expected to be equal. 1284 return []test{{ 1285 label: label + "StructA", 1286 x: ts.StructA{X: "NotEqual"}, 1287 y: ts.StructA{X: "not_equal"}, 1288 }, { 1289 label: label + "StructA", 1290 x: &ts.StructA{X: "NotEqual"}, 1291 y: &ts.StructA{X: "not_equal"}, 1292 }, { 1293 label: label + "StructB", 1294 x: ts.StructB{X: "NotEqual"}, 1295 y: ts.StructB{X: "not_equal"}, 1296 wantDiff: ` 1297 {teststructs.StructB}.X: 1298 -: "NotEqual" 1299 +: "not_equal"`, 1300 }, { 1301 label: label + "StructB", 1302 x: ts.StructB{X: "NotEqual"}, 1303 y: ts.StructB{X: "not_equal"}, 1304 opts: []cmp.Option{derefTransform}, 1305 }, { 1306 label: label + "StructB", 1307 x: &ts.StructB{X: "NotEqual"}, 1308 y: &ts.StructB{X: "not_equal"}, 1309 }, { 1310 label: label + "StructC", 1311 x: ts.StructC{X: "NotEqual"}, 1312 y: ts.StructC{X: "not_equal"}, 1313 }, { 1314 label: label + "StructC", 1315 x: &ts.StructC{X: "NotEqual"}, 1316 y: &ts.StructC{X: "not_equal"}, 1317 }, { 1318 label: label + "StructD", 1319 x: ts.StructD{X: "NotEqual"}, 1320 y: ts.StructD{X: "not_equal"}, 1321 wantDiff: ` 1322 {teststructs.StructD}.X: 1323 -: "NotEqual" 1324 +: "not_equal"`, 1325 }, { 1326 label: label + "StructD", 1327 x: ts.StructD{X: "NotEqual"}, 1328 y: ts.StructD{X: "not_equal"}, 1329 opts: []cmp.Option{derefTransform}, 1330 }, { 1331 label: label + "StructD", 1332 x: &ts.StructD{X: "NotEqual"}, 1333 y: &ts.StructD{X: "not_equal"}, 1334 }, { 1335 label: label + "StructE", 1336 x: ts.StructE{X: "NotEqual"}, 1337 y: ts.StructE{X: "not_equal"}, 1338 wantDiff: ` 1339 {teststructs.StructE}.X: 1340 -: "NotEqual" 1341 +: "not_equal"`, 1342 }, { 1343 label: label + "StructE", 1344 x: ts.StructE{X: "NotEqual"}, 1345 y: ts.StructE{X: "not_equal"}, 1346 opts: []cmp.Option{derefTransform}, 1347 }, { 1348 label: label + "StructE", 1349 x: &ts.StructE{X: "NotEqual"}, 1350 y: &ts.StructE{X: "not_equal"}, 1351 }, { 1352 label: label + "StructF", 1353 x: ts.StructF{X: "NotEqual"}, 1354 y: ts.StructF{X: "not_equal"}, 1355 wantDiff: ` 1356 {teststructs.StructF}.X: 1357 -: "NotEqual" 1358 +: "not_equal"`, 1359 }, { 1360 label: label + "StructF", 1361 x: &ts.StructF{X: "NotEqual"}, 1362 y: &ts.StructF{X: "not_equal"}, 1363 }, { 1364 label: label + "StructA1", 1365 x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"}, 1366 y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"}, 1367 }, { 1368 label: label + "StructA1", 1369 x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"}, 1370 y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"}, 1371 wantDiff: "{teststructs.StructA1}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1372 }, { 1373 label: label + "StructA1", 1374 x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"}, 1375 y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"}, 1376 }, { 1377 label: label + "StructA1", 1378 x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"}, 1379 y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"}, 1380 wantDiff: "{*teststructs.StructA1}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1381 }, { 1382 label: label + "StructB1", 1383 x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"}, 1384 y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"}, 1385 opts: []cmp.Option{derefTransform}, 1386 }, { 1387 label: label + "StructB1", 1388 x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"}, 1389 y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"}, 1390 opts: []cmp.Option{derefTransform}, 1391 wantDiff: "{teststructs.StructB1}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1392 }, { 1393 label: label + "StructB1", 1394 x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"}, 1395 y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"}, 1396 opts: []cmp.Option{derefTransform}, 1397 }, { 1398 label: label + "StructB1", 1399 x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"}, 1400 y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"}, 1401 opts: []cmp.Option{derefTransform}, 1402 wantDiff: "{*teststructs.StructB1}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1403 }, { 1404 label: label + "StructC1", 1405 x: ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"}, 1406 y: ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"}, 1407 }, { 1408 label: label + "StructC1", 1409 x: &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"}, 1410 y: &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"}, 1411 }, { 1412 label: label + "StructD1", 1413 x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, 1414 y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, 1415 wantDiff: ` 1416 {teststructs.StructD1}.StructD.X: 1417 -: "NotEqual" 1418 +: "not_equal" 1419 {teststructs.StructD1}.X: 1420 -: "NotEqual" 1421 +: "not_equal"`, 1422 }, { 1423 label: label + "StructD1", 1424 x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, 1425 y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, 1426 opts: []cmp.Option{derefTransform}, 1427 }, { 1428 label: label + "StructD1", 1429 x: &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"}, 1430 y: &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"}, 1431 }, { 1432 label: label + "StructE1", 1433 x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, 1434 y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, 1435 wantDiff: ` 1436 {teststructs.StructE1}.StructE.X: 1437 -: "NotEqual" 1438 +: "not_equal" 1439 {teststructs.StructE1}.X: 1440 -: "NotEqual" 1441 +: "not_equal"`, 1442 }, { 1443 label: label + "StructE1", 1444 x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, 1445 y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, 1446 opts: []cmp.Option{derefTransform}, 1447 }, { 1448 label: label + "StructE1", 1449 x: &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"}, 1450 y: &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"}, 1451 }, { 1452 label: label + "StructF1", 1453 x: ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"}, 1454 y: ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"}, 1455 wantDiff: ` 1456 {teststructs.StructF1}.StructF.X: 1457 -: "NotEqual" 1458 +: "not_equal" 1459 {teststructs.StructF1}.X: 1460 -: "NotEqual" 1461 +: "not_equal"`, 1462 }, { 1463 label: label + "StructF1", 1464 x: &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"}, 1465 y: &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"}, 1466 }, { 1467 label: label + "StructA2", 1468 x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"}, 1469 y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"}, 1470 }, { 1471 label: label + "StructA2", 1472 x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"}, 1473 y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"}, 1474 wantDiff: "{teststructs.StructA2}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1475 }, { 1476 label: label + "StructA2", 1477 x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"}, 1478 y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"}, 1479 }, { 1480 label: label + "StructA2", 1481 x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"}, 1482 y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"}, 1483 wantDiff: "{*teststructs.StructA2}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1484 }, { 1485 label: label + "StructB2", 1486 x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"}, 1487 y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"}, 1488 }, { 1489 label: label + "StructB2", 1490 x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"}, 1491 y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"}, 1492 wantDiff: "{teststructs.StructB2}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1493 }, { 1494 label: label + "StructB2", 1495 x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"}, 1496 y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"}, 1497 }, { 1498 label: label + "StructB2", 1499 x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"}, 1500 y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"}, 1501 wantDiff: "{*teststructs.StructB2}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1502 }, { 1503 label: label + "StructC2", 1504 x: ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"}, 1505 y: ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"}, 1506 }, { 1507 label: label + "StructC2", 1508 x: &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"}, 1509 y: &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"}, 1510 }, { 1511 label: label + "StructD2", 1512 x: ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"}, 1513 y: ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"}, 1514 }, { 1515 label: label + "StructD2", 1516 x: &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"}, 1517 y: &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"}, 1518 }, { 1519 label: label + "StructE2", 1520 x: ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"}, 1521 y: ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"}, 1522 }, { 1523 label: label + "StructE2", 1524 x: &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"}, 1525 y: &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"}, 1526 }, { 1527 label: label + "StructF2", 1528 x: ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"}, 1529 y: ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"}, 1530 }, { 1531 label: label + "StructF2", 1532 x: &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"}, 1533 y: &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"}, 1534 }, { 1535 label: label + "StructNo", 1536 x: ts.StructNo{X: "NotEqual"}, 1537 y: ts.StructNo{X: "not_equal"}, 1538 wantDiff: "{teststructs.StructNo}.X:\n\t-: \"NotEqual\"\n\t+: \"not_equal\"\n", 1539 }, { 1540 label: label + "AssignA", 1541 x: ts.AssignA(func() int { return 0 }), 1542 y: ts.AssignA(func() int { return 1 }), 1543 }, { 1544 label: label + "AssignB", 1545 x: ts.AssignB(struct{ A int }{0}), 1546 y: ts.AssignB(struct{ A int }{1}), 1547 }, { 1548 label: label + "AssignC", 1549 x: ts.AssignC(make(chan bool)), 1550 y: ts.AssignC(make(chan bool)), 1551 }, { 1552 label: label + "AssignD", 1553 x: ts.AssignD(make(chan bool)), 1554 y: ts.AssignD(make(chan bool)), 1555 }} 1556 } 1557 1558 func project1Tests() []test { 1559 const label = "Project1" 1560 1561 ignoreUnexported := cmpopts.IgnoreUnexported( 1562 ts.EagleImmutable{}, 1563 ts.DreamerImmutable{}, 1564 ts.SlapImmutable{}, 1565 ts.GoatImmutable{}, 1566 ts.DonkeyImmutable{}, 1567 ts.LoveRadius{}, 1568 ts.SummerLove{}, 1569 ts.SummerLoveSummary{}, 1570 ) 1571 1572 createEagle := func() ts.Eagle { 1573 return ts.Eagle{ 1574 Name: "eagle", 1575 Hounds: []string{"buford", "tannen"}, 1576 Desc: "some description", 1577 Dreamers: []ts.Dreamer{{}, { 1578 Name: "dreamer2", 1579 Animal: []interface{}{ 1580 ts.Goat{ 1581 Target: "corporation", 1582 Immutable: &ts.GoatImmutable{ 1583 ID: "southbay", 1584 State: (*pb.Goat_States)(intPtr(5)), 1585 Started: now, 1586 }, 1587 }, 1588 ts.Donkey{}, 1589 }, 1590 Amoeba: 53, 1591 }}, 1592 Slaps: []ts.Slap{{ 1593 Name: "slapID", 1594 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, 1595 Immutable: &ts.SlapImmutable{ 1596 ID: "immutableSlap", 1597 MildSlap: true, 1598 Started: now, 1599 LoveRadius: &ts.LoveRadius{ 1600 Summer: &ts.SummerLove{ 1601 Summary: &ts.SummerLoveSummary{ 1602 Devices: []string{"foo", "bar", "baz"}, 1603 ChangeType: []pb.SummerType{1, 2, 3}, 1604 }, 1605 }, 1606 }, 1607 }, 1608 }}, 1609 Immutable: &ts.EagleImmutable{ 1610 ID: "eagleID", 1611 Birthday: now, 1612 MissingCall: (*pb.Eagle_MissingCalls)(intPtr(55)), 1613 }, 1614 } 1615 } 1616 1617 return []test{{ 1618 label: label, 1619 x: ts.Eagle{Slaps: []ts.Slap{{ 1620 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, 1621 }}}, 1622 y: ts.Eagle{Slaps: []ts.Slap{{ 1623 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, 1624 }}}, 1625 wantPanic: "cannot handle unexported field", 1626 }, { 1627 label: label, 1628 x: ts.Eagle{Slaps: []ts.Slap{{ 1629 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, 1630 }}}, 1631 y: ts.Eagle{Slaps: []ts.Slap{{ 1632 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, 1633 }}}, 1634 opts: []cmp.Option{cmp.Comparer(pb.Equal)}, 1635 }, { 1636 label: label, 1637 x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, { 1638 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}, 1639 }}}, 1640 y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, { 1641 Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}}, 1642 }}}, 1643 opts: []cmp.Option{cmp.Comparer(pb.Equal)}, 1644 wantDiff: "{teststructs.Eagle}.Slaps[4].Args:\n\t-: s\"metadata\"\n\t+: s\"metadata2\"\n", 1645 }, { 1646 label: label, 1647 x: createEagle(), 1648 y: createEagle(), 1649 opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)}, 1650 }, { 1651 label: label, 1652 x: func() ts.Eagle { 1653 eg := createEagle() 1654 eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2" 1655 eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(intPtr(6)) 1656 eg.Slaps[0].Immutable.MildSlap = false 1657 return eg 1658 }(), 1659 y: func() ts.Eagle { 1660 eg := createEagle() 1661 devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices 1662 eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1] 1663 return eg 1664 }(), 1665 opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)}, 1666 wantDiff: ` 1667 {teststructs.Eagle}.Dreamers[1].Animal[0].(teststructs.Goat).Immutable.ID: 1668 -: "southbay2" 1669 +: "southbay" 1670 *{teststructs.Eagle}.Dreamers[1].Animal[0].(teststructs.Goat).Immutable.State: 1671 -: testprotos.Goat_States(6) 1672 +: testprotos.Goat_States(5) 1673 {teststructs.Eagle}.Slaps[0].Immutable.MildSlap: 1674 -: false 1675 +: true 1676 {teststructs.Eagle}.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices[1->?]: 1677 -: "bar" 1678 +: <non-existent> 1679 {teststructs.Eagle}.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices[2->?]: 1680 -: "baz" 1681 +: <non-existent>`, 1682 }} 1683 } 1684 1685 type germSorter []*pb.Germ 1686 1687 func (gs germSorter) Len() int { return len(gs) } 1688 func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() } 1689 func (gs germSorter) Swap(i, j int) { gs[i], gs[j] = gs[j], gs[i] } 1690 1691 func project2Tests() []test { 1692 const label = "Project2" 1693 1694 sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ { 1695 out := append([]*pb.Germ(nil), in...) // Make copy 1696 sort.Sort(germSorter(out)) 1697 return out 1698 }) 1699 1700 equalDish := cmp.Comparer(func(x, y *ts.Dish) bool { 1701 if x == nil || y == nil { 1702 return x == nil && y == nil 1703 } 1704 px, err1 := x.Proto() 1705 py, err2 := y.Proto() 1706 if err1 != nil || err2 != nil { 1707 return err1 == err2 1708 } 1709 return pb.Equal(px, py) 1710 }) 1711 1712 createBatch := func() ts.GermBatch { 1713 return ts.GermBatch{ 1714 DirtyGerms: map[int32][]*pb.Germ{ 1715 17: { 1716 {Stringer: pb.Stringer{X: "germ1"}}, 1717 }, 1718 18: { 1719 {Stringer: pb.Stringer{X: "germ2"}}, 1720 {Stringer: pb.Stringer{X: "germ3"}}, 1721 {Stringer: pb.Stringer{X: "germ4"}}, 1722 }, 1723 }, 1724 GermMap: map[int32]*pb.Germ{ 1725 13: {Stringer: pb.Stringer{X: "germ13"}}, 1726 21: {Stringer: pb.Stringer{X: "germ21"}}, 1727 }, 1728 DishMap: map[int32]*ts.Dish{ 1729 0: ts.CreateDish(nil, io.EOF), 1730 1: ts.CreateDish(nil, io.ErrUnexpectedEOF), 1731 2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil), 1732 }, 1733 HasPreviousResult: true, 1734 DirtyID: 10, 1735 GermStrain: 421, 1736 InfectedAt: now, 1737 } 1738 } 1739 1740 return []test{{ 1741 label: label, 1742 x: createBatch(), 1743 y: createBatch(), 1744 wantPanic: "cannot handle unexported field", 1745 }, { 1746 label: label, 1747 x: createBatch(), 1748 y: createBatch(), 1749 opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, 1750 }, { 1751 label: label, 1752 x: createBatch(), 1753 y: func() ts.GermBatch { 1754 gb := createBatch() 1755 s := gb.DirtyGerms[18] 1756 s[0], s[1], s[2] = s[1], s[2], s[0] 1757 return gb 1758 }(), 1759 opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish}, 1760 wantDiff: ` 1761 {teststructs.GermBatch}.DirtyGerms[18][0->?]: 1762 -: s"germ2" 1763 +: <non-existent> 1764 {teststructs.GermBatch}.DirtyGerms[18][?->2]: 1765 -: <non-existent> 1766 +: s"germ2"`, 1767 }, { 1768 label: label, 1769 x: createBatch(), 1770 y: func() ts.GermBatch { 1771 gb := createBatch() 1772 s := gb.DirtyGerms[18] 1773 s[0], s[1], s[2] = s[1], s[2], s[0] 1774 return gb 1775 }(), 1776 opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, 1777 }, { 1778 label: label, 1779 x: func() ts.GermBatch { 1780 gb := createBatch() 1781 delete(gb.DirtyGerms, 17) 1782 gb.DishMap[1] = nil 1783 return gb 1784 }(), 1785 y: func() ts.GermBatch { 1786 gb := createBatch() 1787 gb.DirtyGerms[18] = gb.DirtyGerms[18][:2] 1788 gb.GermStrain = 22 1789 return gb 1790 }(), 1791 opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish}, 1792 wantDiff: ` 1793 {teststructs.GermBatch}.DirtyGerms[17]: 1794 -: <non-existent> 1795 +: []*testprotos.Germ{s"germ1"} 1796 Sort({teststructs.GermBatch}.DirtyGerms[18])[2->?]: 1797 -: s"germ4" 1798 +: <non-existent> 1799 {teststructs.GermBatch}.DishMap[1]: 1800 -: (*teststructs.Dish)(nil) 1801 +: &teststructs.Dish{err: &errors.errorString{s: "unexpected EOF"}} 1802 {teststructs.GermBatch}.GermStrain: 1803 -: 421 1804 +: 22`, 1805 }} 1806 } 1807 1808 func project3Tests() []test { 1809 const label = "Project3" 1810 1811 allowVisibility := cmp.AllowUnexported(ts.Dirt{}) 1812 1813 ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{}) 1814 1815 transformProtos := cmp.Transformer("", func(x pb.Dirt) *pb.Dirt { 1816 return &x 1817 }) 1818 1819 equalTable := cmp.Comparer(func(x, y ts.Table) bool { 1820 tx, ok1 := x.(*ts.MockTable) 1821 ty, ok2 := y.(*ts.MockTable) 1822 if !ok1 || !ok2 { 1823 panic("table type must be MockTable") 1824 } 1825 return cmp.Equal(tx.State(), ty.State()) 1826 }) 1827 1828 createDirt := func() (d ts.Dirt) { 1829 d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"})) 1830 d.SetTimestamp(12345) 1831 d.Discord = 554 1832 d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}} 1833 d.SetWizard(map[string]*pb.Wizard{ 1834 "harry": {Stringer: pb.Stringer{X: "potter"}}, 1835 "albus": {Stringer: pb.Stringer{X: "dumbledore"}}, 1836 }) 1837 d.SetLastTime(54321) 1838 return d 1839 } 1840 1841 return []test{{ 1842 label: label, 1843 x: createDirt(), 1844 y: createDirt(), 1845 wantPanic: "cannot handle unexported field", 1846 }, { 1847 label: label, 1848 x: createDirt(), 1849 y: createDirt(), 1850 opts: []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, 1851 wantPanic: "cannot handle unexported field", 1852 }, { 1853 label: label, 1854 x: createDirt(), 1855 y: createDirt(), 1856 opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, 1857 }, { 1858 label: label, 1859 x: func() ts.Dirt { 1860 d := createDirt() 1861 d.SetTable(ts.CreateMockTable([]string{"a", "c"})) 1862 d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}} 1863 return d 1864 }(), 1865 y: func() ts.Dirt { 1866 d := createDirt() 1867 d.Discord = 500 1868 d.SetWizard(map[string]*pb.Wizard{ 1869 "harry": {Stringer: pb.Stringer{X: "otter"}}, 1870 }) 1871 return d 1872 }(), 1873 opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable}, 1874 wantDiff: ` 1875 {teststructs.Dirt}.table: 1876 -: &teststructs.MockTable{state: []string{"a", "c"}} 1877 +: &teststructs.MockTable{state: []string{"a", "b", "c"}} 1878 {teststructs.Dirt}.Discord: 1879 -: teststructs.DiscordState(554) 1880 +: teststructs.DiscordState(500) 1881 λ({teststructs.Dirt}.Proto): 1882 -: s"blah" 1883 +: s"proto" 1884 {teststructs.Dirt}.wizard["albus"]: 1885 -: s"dumbledore" 1886 +: <non-existent> 1887 {teststructs.Dirt}.wizard["harry"]: 1888 -: s"potter" 1889 +: s"otter"`, 1890 }} 1891 } 1892 1893 func project4Tests() []test { 1894 const label = "Project4" 1895 1896 allowVisibility := cmp.AllowUnexported( 1897 ts.Cartel{}, 1898 ts.Headquarter{}, 1899 ts.Poison{}, 1900 ) 1901 1902 transformProtos := cmp.Transformer("", func(x pb.Restrictions) *pb.Restrictions { 1903 return &x 1904 }) 1905 1906 createCartel := func() ts.Cartel { 1907 var p ts.Poison 1908 p.SetPoisonType(5) 1909 p.SetExpiration(now) 1910 p.SetManufacturer("acme") 1911 1912 var hq ts.Headquarter 1913 hq.SetID(5) 1914 hq.SetLocation("moon") 1915 hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"}) 1916 hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}}) 1917 hq.SetPublicMessage([]byte{1, 2, 3, 4, 5}) 1918 hq.SetHorseBack("abcdef") 1919 hq.SetStatus(44) 1920 1921 var c ts.Cartel 1922 c.Headquarter = hq 1923 c.SetSource("mars") 1924 c.SetCreationTime(now) 1925 c.SetBoss("al capone") 1926 c.SetPoisons([]*ts.Poison{&p}) 1927 1928 return c 1929 } 1930 1931 return []test{{ 1932 label: label, 1933 x: createCartel(), 1934 y: createCartel(), 1935 wantPanic: "cannot handle unexported field", 1936 }, { 1937 label: label, 1938 x: createCartel(), 1939 y: createCartel(), 1940 opts: []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)}, 1941 wantPanic: "cannot handle unexported field", 1942 }, { 1943 label: label, 1944 x: createCartel(), 1945 y: createCartel(), 1946 opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)}, 1947 }, { 1948 label: label, 1949 x: func() ts.Cartel { 1950 d := createCartel() 1951 var p1, p2 ts.Poison 1952 p1.SetPoisonType(1) 1953 p1.SetExpiration(now) 1954 p1.SetManufacturer("acme") 1955 p2.SetPoisonType(2) 1956 p2.SetManufacturer("acme2") 1957 d.SetPoisons([]*ts.Poison{&p1, &p2}) 1958 return d 1959 }(), 1960 y: func() ts.Cartel { 1961 d := createCartel() 1962 d.SetSubDivisions([]string{"bravo", "charlie"}) 1963 d.SetPublicMessage([]byte{1, 2, 4, 3, 5}) 1964 return d 1965 }(), 1966 opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)}, 1967 wantDiff: ` 1968 {teststructs.Cartel}.Headquarter.subDivisions[0->?]: 1969 -: "alpha" 1970 +: <non-existent> 1971 {teststructs.Cartel}.Headquarter.publicMessage[2]: 1972 -: 0x03 1973 +: 0x04 1974 {teststructs.Cartel}.Headquarter.publicMessage[3]: 1975 -: 0x04 1976 +: 0x03 1977 {teststructs.Cartel}.poisons[0].poisonType: 1978 -: testprotos.PoisonType(1) 1979 +: testprotos.PoisonType(5) 1980 {teststructs.Cartel}.poisons[1->?]: 1981 -: &teststructs.Poison{poisonType: testprotos.PoisonType(2), manufacturer: "acme2"} 1982 +: <non-existent>`, 1983 }} 1984 }