You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

compare_test.go 80 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829
  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. package cmp_test
  5. import (
  6. "bytes"
  7. "crypto/md5"
  8. "encoding/json"
  9. "fmt"
  10. "io"
  11. "math"
  12. "math/rand"
  13. "reflect"
  14. "regexp"
  15. "sort"
  16. "strings"
  17. "sync"
  18. "testing"
  19. "time"
  20. "github.com/google/go-cmp/cmp"
  21. "github.com/google/go-cmp/cmp/cmpopts"
  22. "github.com/google/go-cmp/cmp/internal/flags"
  23. pb "github.com/google/go-cmp/cmp/internal/testprotos"
  24. ts "github.com/google/go-cmp/cmp/internal/teststructs"
  25. )
  26. func init() {
  27. flags.Deterministic = true
  28. }
  29. var now = time.Date(2009, time.November, 10, 23, 00, 00, 00, time.UTC)
  30. func intPtr(n int) *int { return &n }
  31. type test struct {
  32. label string // Test name
  33. x, y interface{} // Input values to compare
  34. opts []cmp.Option // Input options
  35. wantDiff string // The exact difference string
  36. wantPanic string // Sub-string of an expected panic message
  37. reason string // The reason for the expected outcome
  38. }
  39. func TestDiff(t *testing.T) {
  40. var tests []test
  41. tests = append(tests, comparerTests()...)
  42. tests = append(tests, transformerTests()...)
  43. tests = append(tests, embeddedTests()...)
  44. tests = append(tests, methodTests()...)
  45. tests = append(tests, project1Tests()...)
  46. tests = append(tests, project2Tests()...)
  47. tests = append(tests, project3Tests()...)
  48. tests = append(tests, project4Tests()...)
  49. for _, tt := range tests {
  50. tt := tt
  51. t.Run(tt.label, func(t *testing.T) {
  52. t.Parallel()
  53. var gotDiff, gotPanic string
  54. func() {
  55. defer func() {
  56. if ex := recover(); ex != nil {
  57. if s, ok := ex.(string); ok {
  58. gotPanic = s
  59. } else {
  60. panic(ex)
  61. }
  62. }
  63. }()
  64. gotDiff = cmp.Diff(tt.x, tt.y, tt.opts...)
  65. }()
  66. // TODO: Require every test case to provide a reason.
  67. if tt.wantPanic == "" {
  68. if gotPanic != "" {
  69. t.Fatalf("unexpected panic message: %s\nreason: %v", gotPanic, tt.reason)
  70. }
  71. tt.wantDiff = strings.TrimPrefix(tt.wantDiff, "\n")
  72. if gotDiff != tt.wantDiff {
  73. t.Fatalf("difference message:\ngot:\n%s\nwant:\n%s\nreason: %v", gotDiff, tt.wantDiff, tt.reason)
  74. }
  75. } else {
  76. if !strings.Contains(gotPanic, tt.wantPanic) {
  77. t.Fatalf("panic message:\ngot: %s\nwant: %s\nreason: %v", gotPanic, tt.wantPanic, tt.reason)
  78. }
  79. }
  80. })
  81. }
  82. }
  83. func comparerTests() []test {
  84. const label = "Comparer"
  85. type Iface1 interface {
  86. Method()
  87. }
  88. type Iface2 interface {
  89. Method()
  90. }
  91. type tarHeader struct {
  92. Name string
  93. Mode int64
  94. Uid int
  95. Gid int
  96. Size int64
  97. ModTime time.Time
  98. Typeflag byte
  99. Linkname string
  100. Uname string
  101. Gname string
  102. Devmajor int64
  103. Devminor int64
  104. AccessTime time.Time
  105. ChangeTime time.Time
  106. Xattrs map[string]string
  107. }
  108. makeTarHeaders := func(tf byte) (hs []tarHeader) {
  109. for i := 0; i < 5; i++ {
  110. hs = append(hs, tarHeader{
  111. Name: fmt.Sprintf("some/dummy/test/file%d", i),
  112. Mode: 0664, Uid: i * 1000, Gid: i * 1000, Size: 1 << uint(i),
  113. ModTime: now.Add(time.Duration(i) * time.Hour),
  114. Uname: "user", Gname: "group",
  115. Typeflag: tf,
  116. })
  117. }
  118. return hs
  119. }
  120. return []test{{
  121. label: label,
  122. x: nil,
  123. y: nil,
  124. }, {
  125. label: label,
  126. x: 1,
  127. y: 1,
  128. }, {
  129. label: label,
  130. x: 1,
  131. y: 1,
  132. opts: []cmp.Option{cmp.Ignore()},
  133. wantPanic: "cannot use an unfiltered option",
  134. }, {
  135. label: label,
  136. x: 1,
  137. y: 1,
  138. opts: []cmp.Option{cmp.Comparer(func(_, _ interface{}) bool { return true })},
  139. wantPanic: "cannot use an unfiltered option",
  140. }, {
  141. label: label,
  142. x: 1,
  143. y: 1,
  144. opts: []cmp.Option{cmp.Transformer("λ", func(x interface{}) interface{} { return x })},
  145. wantPanic: "cannot use an unfiltered option",
  146. }, {
  147. label: label,
  148. x: 1,
  149. y: 1,
  150. opts: []cmp.Option{
  151. cmp.Comparer(func(x, y int) bool { return true }),
  152. cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
  153. },
  154. wantPanic: "ambiguous set of applicable options",
  155. }, {
  156. label: label,
  157. x: 1,
  158. y: 1,
  159. opts: []cmp.Option{
  160. cmp.FilterPath(func(p cmp.Path) bool {
  161. return len(p) > 0 && p[len(p)-1].Type().Kind() == reflect.Int
  162. }, cmp.Options{cmp.Ignore(), cmp.Ignore(), cmp.Ignore()}),
  163. cmp.Comparer(func(x, y int) bool { return true }),
  164. cmp.Transformer("λ", func(x int) float64 { return float64(x) }),
  165. },
  166. }, {
  167. label: label,
  168. opts: []cmp.Option{struct{ cmp.Option }{}},
  169. wantPanic: "unknown option",
  170. }, {
  171. label: label,
  172. x: struct{ A, B, C int }{1, 2, 3},
  173. y: struct{ A, B, C int }{1, 2, 3},
  174. }, {
  175. label: label,
  176. x: struct{ A, B, C int }{1, 2, 3},
  177. y: struct{ A, B, C int }{1, 2, 4},
  178. wantDiff: `
  179. struct{ A int; B int; C int }{
  180. A: 1,
  181. B: 2,
  182. - C: 3,
  183. + C: 4,
  184. }
  185. `,
  186. }, {
  187. label: label,
  188. x: struct{ a, b, c int }{1, 2, 3},
  189. y: struct{ a, b, c int }{1, 2, 4},
  190. wantPanic: "cannot handle unexported field",
  191. }, {
  192. label: label,
  193. x: &struct{ A *int }{intPtr(4)},
  194. y: &struct{ A *int }{intPtr(4)},
  195. }, {
  196. label: label,
  197. x: &struct{ A *int }{intPtr(4)},
  198. y: &struct{ A *int }{intPtr(5)},
  199. wantDiff: `
  200. &struct{ A *int }{
  201. - A: &4,
  202. + A: &5,
  203. }
  204. `,
  205. }, {
  206. label: label,
  207. x: &struct{ A *int }{intPtr(4)},
  208. y: &struct{ A *int }{intPtr(5)},
  209. opts: []cmp.Option{
  210. cmp.Comparer(func(x, y int) bool { return true }),
  211. },
  212. }, {
  213. label: label,
  214. x: &struct{ A *int }{intPtr(4)},
  215. y: &struct{ A *int }{intPtr(5)},
  216. opts: []cmp.Option{
  217. cmp.Comparer(func(x, y *int) bool { return x != nil && y != nil }),
  218. },
  219. }, {
  220. label: label,
  221. x: &struct{ R *bytes.Buffer }{},
  222. y: &struct{ R *bytes.Buffer }{},
  223. }, {
  224. label: label,
  225. x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
  226. y: &struct{ R *bytes.Buffer }{},
  227. wantDiff: `
  228. &struct{ R *bytes.Buffer }{
  229. - R: s"",
  230. + R: nil,
  231. }
  232. `,
  233. }, {
  234. label: label,
  235. x: &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
  236. y: &struct{ R *bytes.Buffer }{},
  237. opts: []cmp.Option{
  238. cmp.Comparer(func(x, y io.Reader) bool { return true }),
  239. },
  240. }, {
  241. label: label,
  242. x: &struct{ R bytes.Buffer }{},
  243. y: &struct{ R bytes.Buffer }{},
  244. wantPanic: "cannot handle unexported field",
  245. }, {
  246. label: label,
  247. x: &struct{ R bytes.Buffer }{},
  248. y: &struct{ R bytes.Buffer }{},
  249. opts: []cmp.Option{
  250. cmp.Comparer(func(x, y io.Reader) bool { return true }),
  251. },
  252. wantPanic: "cannot handle unexported field",
  253. }, {
  254. label: label,
  255. x: &struct{ R bytes.Buffer }{},
  256. y: &struct{ R bytes.Buffer }{},
  257. opts: []cmp.Option{
  258. cmp.Transformer("Ref", func(x bytes.Buffer) *bytes.Buffer { return &x }),
  259. cmp.Comparer(func(x, y io.Reader) bool { return true }),
  260. },
  261. }, {
  262. label: label,
  263. x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
  264. y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
  265. wantPanic: "cannot handle unexported field",
  266. }, {
  267. label: label,
  268. x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
  269. y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
  270. opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool {
  271. if x == nil || y == nil {
  272. return x == nil && y == nil
  273. }
  274. return x.String() == y.String()
  275. })},
  276. }, {
  277. label: label,
  278. x: []*regexp.Regexp{nil, regexp.MustCompile("a*b*c*")},
  279. y: []*regexp.Regexp{nil, regexp.MustCompile("a*b*d*")},
  280. opts: []cmp.Option{cmp.Comparer(func(x, y *regexp.Regexp) bool {
  281. if x == nil || y == nil {
  282. return x == nil && y == nil
  283. }
  284. return x.String() == y.String()
  285. })},
  286. wantDiff: `
  287. []*regexp.Regexp{
  288. nil,
  289. - s"a*b*c*",
  290. + s"a*b*d*",
  291. }
  292. `,
  293. }, {
  294. label: label,
  295. x: func() ***int {
  296. a := 0
  297. b := &a
  298. c := &b
  299. return &c
  300. }(),
  301. y: func() ***int {
  302. a := 0
  303. b := &a
  304. c := &b
  305. return &c
  306. }(),
  307. }, {
  308. label: label,
  309. x: func() ***int {
  310. a := 0
  311. b := &a
  312. c := &b
  313. return &c
  314. }(),
  315. y: func() ***int {
  316. a := 1
  317. b := &a
  318. c := &b
  319. return &c
  320. }(),
  321. wantDiff: `
  322. &&&int(
  323. - 0,
  324. + 1,
  325. )
  326. `,
  327. }, {
  328. label: label,
  329. x: []int{1, 2, 3, 4, 5}[:3],
  330. y: []int{1, 2, 3},
  331. }, {
  332. label: label,
  333. x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")},
  334. y: struct{ fmt.Stringer }{regexp.MustCompile("hello")},
  335. opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
  336. }, {
  337. label: label,
  338. x: struct{ fmt.Stringer }{bytes.NewBufferString("hello")},
  339. y: struct{ fmt.Stringer }{regexp.MustCompile("hello2")},
  340. opts: []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
  341. wantDiff: `
  342. struct{ fmt.Stringer }(
  343. - s"hello",
  344. + s"hello2",
  345. )
  346. `,
  347. }, {
  348. label: label,
  349. x: md5.Sum([]byte{'a'}),
  350. y: md5.Sum([]byte{'b'}),
  351. wantDiff: `
  352. [16]uint8{
  353. - 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61,
  354. + 0x92, 0xeb, 0x5f, 0xfe, 0xe6, 0xae, 0x2f, 0xec, 0x3a, 0xd7, 0x1c, 0x77, 0x75, 0x31, 0x57, 0x8f,
  355. }
  356. `,
  357. }, {
  358. label: label,
  359. x: new(fmt.Stringer),
  360. y: nil,
  361. wantDiff: `
  362. interface{}(
  363. - &fmt.Stringer(nil),
  364. )
  365. `,
  366. }, {
  367. label: label,
  368. x: makeTarHeaders('0'),
  369. y: makeTarHeaders('\x00'),
  370. wantDiff: `
  371. []cmp_test.tarHeader{
  372. {
  373. ... // 4 identical fields
  374. Size: 1,
  375. ModTime: s"2009-11-10 23:00:00 +0000 UTC",
  376. - Typeflag: 0x30,
  377. + Typeflag: 0x00,
  378. Linkname: "",
  379. Uname: "user",
  380. ... // 6 identical fields
  381. },
  382. {
  383. ... // 4 identical fields
  384. Size: 2,
  385. ModTime: s"2009-11-11 00:00:00 +0000 UTC",
  386. - Typeflag: 0x30,
  387. + Typeflag: 0x00,
  388. Linkname: "",
  389. Uname: "user",
  390. ... // 6 identical fields
  391. },
  392. {
  393. ... // 4 identical fields
  394. Size: 4,
  395. ModTime: s"2009-11-11 01:00:00 +0000 UTC",
  396. - Typeflag: 0x30,
  397. + Typeflag: 0x00,
  398. Linkname: "",
  399. Uname: "user",
  400. ... // 6 identical fields
  401. },
  402. {
  403. ... // 4 identical fields
  404. Size: 8,
  405. ModTime: s"2009-11-11 02:00:00 +0000 UTC",
  406. - Typeflag: 0x30,
  407. + Typeflag: 0x00,
  408. Linkname: "",
  409. Uname: "user",
  410. ... // 6 identical fields
  411. },
  412. {
  413. ... // 4 identical fields
  414. Size: 16,
  415. ModTime: s"2009-11-11 03:00:00 +0000 UTC",
  416. - Typeflag: 0x30,
  417. + Typeflag: 0x00,
  418. Linkname: "",
  419. Uname: "user",
  420. ... // 6 identical fields
  421. },
  422. }
  423. `,
  424. }, {
  425. label: label,
  426. x: make([]int, 1000),
  427. y: make([]int, 1000),
  428. opts: []cmp.Option{
  429. cmp.Comparer(func(_, _ int) bool {
  430. return rand.Intn(2) == 0
  431. }),
  432. },
  433. wantPanic: "non-deterministic or non-symmetric function detected",
  434. }, {
  435. label: label,
  436. x: make([]int, 1000),
  437. y: make([]int, 1000),
  438. opts: []cmp.Option{
  439. cmp.FilterValues(func(_, _ int) bool {
  440. return rand.Intn(2) == 0
  441. }, cmp.Ignore()),
  442. },
  443. wantPanic: "non-deterministic or non-symmetric function detected",
  444. }, {
  445. label: label,
  446. x: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
  447. y: []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
  448. opts: []cmp.Option{
  449. cmp.Comparer(func(x, y int) bool {
  450. return x < y
  451. }),
  452. },
  453. wantPanic: "non-deterministic or non-symmetric function detected",
  454. }, {
  455. label: label,
  456. x: make([]string, 1000),
  457. y: make([]string, 1000),
  458. opts: []cmp.Option{
  459. cmp.Transformer("λ", func(x string) int {
  460. return rand.Int()
  461. }),
  462. },
  463. wantPanic: "non-deterministic function detected",
  464. }, {
  465. // Make sure the dynamic checks don't raise a false positive for
  466. // non-reflexive comparisons.
  467. label: label,
  468. x: make([]int, 10),
  469. y: make([]int, 10),
  470. opts: []cmp.Option{
  471. cmp.Transformer("λ", func(x int) float64 {
  472. return math.NaN()
  473. }),
  474. },
  475. wantDiff: `
  476. []int{
  477. - Inverse(λ, float64(NaN)),
  478. + Inverse(λ, float64(NaN)),
  479. - Inverse(λ, float64(NaN)),
  480. + Inverse(λ, float64(NaN)),
  481. - Inverse(λ, float64(NaN)),
  482. + Inverse(λ, float64(NaN)),
  483. - Inverse(λ, float64(NaN)),
  484. + Inverse(λ, float64(NaN)),
  485. - Inverse(λ, float64(NaN)),
  486. + Inverse(λ, float64(NaN)),
  487. - Inverse(λ, float64(NaN)),
  488. + Inverse(λ, float64(NaN)),
  489. - Inverse(λ, float64(NaN)),
  490. + Inverse(λ, float64(NaN)),
  491. - Inverse(λ, float64(NaN)),
  492. + Inverse(λ, float64(NaN)),
  493. - Inverse(λ, float64(NaN)),
  494. + Inverse(λ, float64(NaN)),
  495. - Inverse(λ, float64(NaN)),
  496. + Inverse(λ, float64(NaN)),
  497. }
  498. `,
  499. }, {
  500. // Ensure reasonable Stringer formatting of map keys.
  501. label: label,
  502. x: map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}},
  503. y: map[*pb.Stringer]*pb.Stringer(nil),
  504. wantDiff: `
  505. map[*testprotos.Stringer]*testprotos.Stringer(
  506. - {⟪0xdeadf00f⟫: s"world"},
  507. + nil,
  508. )
  509. `,
  510. }, {
  511. // Ensure Stringer avoids double-quote escaping if possible.
  512. label: label,
  513. x: []*pb.Stringer{{`multi\nline\nline\nline`}},
  514. wantDiff: strings.Replace(`
  515. interface{}(
  516. - []*testprotos.Stringer{s'multi\nline\nline\nline'},
  517. )
  518. `, "'", "`", -1),
  519. }, {
  520. label: label,
  521. x: struct{ I Iface2 }{},
  522. y: struct{ I Iface2 }{},
  523. opts: []cmp.Option{
  524. cmp.Comparer(func(x, y Iface1) bool {
  525. return x == nil && y == nil
  526. }),
  527. },
  528. }, {
  529. label: label,
  530. x: struct{ I Iface2 }{},
  531. y: struct{ I Iface2 }{},
  532. opts: []cmp.Option{
  533. cmp.Transformer("λ", func(v Iface1) bool {
  534. return v == nil
  535. }),
  536. },
  537. }, {
  538. label: label,
  539. x: struct{ I Iface2 }{},
  540. y: struct{ I Iface2 }{},
  541. opts: []cmp.Option{
  542. cmp.FilterValues(func(x, y Iface1) bool {
  543. return x == nil && y == nil
  544. }, cmp.Ignore()),
  545. },
  546. }, {
  547. label: label,
  548. x: []interface{}{map[string]interface{}{"avg": 0.278, "hr": 65, "name": "Mark McGwire"}, map[string]interface{}{"avg": 0.288, "hr": 63, "name": "Sammy Sosa"}},
  549. 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"}},
  550. wantDiff: `
  551. []interface{}{
  552. map[string]interface{}{
  553. "avg": float64(0.278),
  554. - "hr": int(65),
  555. + "hr": float64(65),
  556. "name": string("Mark McGwire"),
  557. },
  558. map[string]interface{}{
  559. "avg": float64(0.288),
  560. - "hr": int(63),
  561. + "hr": float64(63),
  562. "name": string("Sammy Sosa"),
  563. },
  564. }
  565. `,
  566. }, {
  567. label: label,
  568. x: map[*int]string{
  569. new(int): "hello",
  570. },
  571. y: map[*int]string{
  572. new(int): "world",
  573. },
  574. wantDiff: `
  575. map[*int]string{
  576. - ⟪0xdeadf00f⟫: "hello",
  577. + ⟪0xdeadf00f⟫: "world",
  578. }
  579. `,
  580. }, {
  581. label: label,
  582. x: intPtr(0),
  583. y: intPtr(0),
  584. opts: []cmp.Option{
  585. cmp.Comparer(func(x, y *int) bool { return x == y }),
  586. },
  587. // TODO: This output is unhelpful and should show the address.
  588. wantDiff: `
  589. (*int)(
  590. - &0,
  591. + &0,
  592. )
  593. `,
  594. }, {
  595. label: label,
  596. x: [2][]int{
  597. {0, 0, 0, 1, 2, 3, 0, 0, 4, 5, 6, 7, 8, 0, 9, 0, 0},
  598. {0, 1, 0, 0, 0, 20},
  599. },
  600. y: [2][]int{
  601. {1, 2, 3, 0, 4, 5, 6, 7, 0, 8, 9, 0, 0, 0},
  602. {0, 0, 1, 2, 0, 0, 0},
  603. },
  604. opts: []cmp.Option{
  605. cmp.FilterPath(func(p cmp.Path) bool {
  606. vx, vy := p.Last().Values()
  607. if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 {
  608. return true
  609. }
  610. if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
  611. return true
  612. }
  613. return false
  614. }, cmp.Ignore()),
  615. },
  616. wantDiff: `
  617. [2][]int{
  618. {..., 1, 2, 3, ..., 4, 5, 6, 7, ..., 8, ..., 9, ...},
  619. {
  620. ... // 6 ignored and 1 identical elements
  621. - 20,
  622. + 2,
  623. ... // 3 ignored elements
  624. },
  625. }
  626. `,
  627. reason: "all zero slice elements are ignored (even if missing)",
  628. }, {
  629. label: label,
  630. x: [2]map[string]int{
  631. {"ignore1": 0, "ignore2": 0, "keep1": 1, "keep2": 2, "KEEP3": 3, "IGNORE3": 0},
  632. {"keep1": 1, "ignore1": 0},
  633. },
  634. y: [2]map[string]int{
  635. {"ignore1": 0, "ignore3": 0, "ignore4": 0, "keep1": 1, "keep2": 2, "KEEP3": 3},
  636. {"keep1": 1, "keep2": 2, "ignore2": 0},
  637. },
  638. opts: []cmp.Option{
  639. cmp.FilterPath(func(p cmp.Path) bool {
  640. vx, vy := p.Last().Values()
  641. if vx.IsValid() && vx.Kind() == reflect.Int && vx.Int() == 0 {
  642. return true
  643. }
  644. if vy.IsValid() && vy.Kind() == reflect.Int && vy.Int() == 0 {
  645. return true
  646. }
  647. return false
  648. }, cmp.Ignore()),
  649. },
  650. wantDiff: `
  651. [2]map[string]int{
  652. {"KEEP3": 3, "keep1": 1, "keep2": 2, ...},
  653. {
  654. ... // 2 ignored entries
  655. "keep1": 1,
  656. + "keep2": 2,
  657. },
  658. }
  659. `,
  660. reason: "all zero map entries are ignored (even if missing)",
  661. }}
  662. }
  663. func transformerTests() []test {
  664. type StringBytes struct {
  665. String string
  666. Bytes []byte
  667. }
  668. const label = "Transformer"
  669. transformOnce := func(name string, f interface{}) cmp.Option {
  670. xform := cmp.Transformer(name, f)
  671. return cmp.FilterPath(func(p cmp.Path) bool {
  672. for _, ps := range p {
  673. if tr, ok := ps.(cmp.Transform); ok && tr.Option() == xform {
  674. return false
  675. }
  676. }
  677. return true
  678. }, xform)
  679. }
  680. return []test{{
  681. label: label,
  682. x: uint8(0),
  683. y: uint8(1),
  684. opts: []cmp.Option{
  685. cmp.Transformer("λ", func(in uint8) uint16 { return uint16(in) }),
  686. cmp.Transformer("λ", func(in uint16) uint32 { return uint32(in) }),
  687. cmp.Transformer("λ", func(in uint32) uint64 { return uint64(in) }),
  688. },
  689. wantDiff: `
  690. uint8(Inverse(λ, uint16(Inverse(λ, uint32(Inverse(λ, uint64(
  691. - 0x00,
  692. + 0x01,
  693. )))))))
  694. `,
  695. }, {
  696. label: label,
  697. x: 0,
  698. y: 1,
  699. opts: []cmp.Option{
  700. cmp.Transformer("λ", func(in int) int { return in / 2 }),
  701. cmp.Transformer("λ", func(in int) int { return in }),
  702. },
  703. wantPanic: "ambiguous set of applicable options",
  704. }, {
  705. label: label,
  706. x: []int{0, -5, 0, -1},
  707. y: []int{1, 3, 0, -5},
  708. opts: []cmp.Option{
  709. cmp.FilterValues(
  710. func(x, y int) bool { return x+y >= 0 },
  711. cmp.Transformer("λ", func(in int) int64 { return int64(in / 2) }),
  712. ),
  713. cmp.FilterValues(
  714. func(x, y int) bool { return x+y < 0 },
  715. cmp.Transformer("λ", func(in int) int64 { return int64(in) }),
  716. ),
  717. },
  718. wantDiff: `
  719. []int{
  720. Inverse(λ, int64(0)),
  721. - Inverse(λ, int64(-5)),
  722. + Inverse(λ, int64(3)),
  723. Inverse(λ, int64(0)),
  724. - Inverse(λ, int64(-1)),
  725. + Inverse(λ, int64(-5)),
  726. }
  727. `,
  728. }, {
  729. label: label,
  730. x: 0,
  731. y: 1,
  732. opts: []cmp.Option{
  733. cmp.Transformer("λ", func(in int) interface{} {
  734. if in == 0 {
  735. return "zero"
  736. }
  737. return float64(in)
  738. }),
  739. },
  740. wantDiff: `
  741. int(Inverse(λ, interface{}(
  742. - string("zero"),
  743. + float64(1),
  744. )))
  745. `,
  746. }, {
  747. label: label,
  748. x: `{
  749. "firstName": "John",
  750. "lastName": "Smith",
  751. "age": 25,
  752. "isAlive": true,
  753. "address": {
  754. "city": "Los Angeles",
  755. "postalCode": "10021-3100",
  756. "state": "CA",
  757. "streetAddress": "21 2nd Street"
  758. },
  759. "phoneNumbers": [{
  760. "type": "home",
  761. "number": "212 555-4321"
  762. },{
  763. "type": "office",
  764. "number": "646 555-4567"
  765. },{
  766. "number": "123 456-7890",
  767. "type": "mobile"
  768. }],
  769. "children": []
  770. }`,
  771. y: `{"firstName":"John","lastName":"Smith","isAlive":true,"age":25,
  772. "address":{"streetAddress":"21 2nd Street","city":"New York",
  773. "state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home",
  774. "number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{
  775. "type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`,
  776. opts: []cmp.Option{
  777. transformOnce("ParseJSON", func(s string) (m map[string]interface{}) {
  778. if err := json.Unmarshal([]byte(s), &m); err != nil {
  779. panic(err)
  780. }
  781. return m
  782. }),
  783. },
  784. wantDiff: `
  785. string(Inverse(ParseJSON, map[string]interface{}{
  786. "address": map[string]interface{}{
  787. - "city": string("Los Angeles"),
  788. + "city": string("New York"),
  789. "postalCode": string("10021-3100"),
  790. - "state": string("CA"),
  791. + "state": string("NY"),
  792. "streetAddress": string("21 2nd Street"),
  793. },
  794. "age": float64(25),
  795. "children": []interface{}{},
  796. "firstName": string("John"),
  797. "isAlive": bool(true),
  798. "lastName": string("Smith"),
  799. "phoneNumbers": []interface{}{
  800. map[string]interface{}{
  801. - "number": string("212 555-4321"),
  802. + "number": string("212 555-1234"),
  803. "type": string("home"),
  804. },
  805. map[string]interface{}{"number": string("646 555-4567"), "type": string("office")},
  806. map[string]interface{}{"number": string("123 456-7890"), "type": string("mobile")},
  807. },
  808. + "spouse": nil,
  809. }))
  810. `,
  811. }, {
  812. label: label,
  813. x: StringBytes{String: "some\nmulti\nLine\nstring", Bytes: []byte("some\nmulti\nline\nbytes")},
  814. y: StringBytes{String: "some\nmulti\nline\nstring", Bytes: []byte("some\nmulti\nline\nBytes")},
  815. opts: []cmp.Option{
  816. transformOnce("SplitString", func(s string) []string { return strings.Split(s, "\n") }),
  817. transformOnce("SplitBytes", func(b []byte) [][]byte { return bytes.Split(b, []byte("\n")) }),
  818. },
  819. wantDiff: `
  820. cmp_test.StringBytes{
  821. String: Inverse(SplitString, []string{
  822. "some",
  823. "multi",
  824. - "Line",
  825. + "line",
  826. "string",
  827. }),
  828. Bytes: []uint8(Inverse(SplitBytes, [][]uint8{
  829. {0x73, 0x6f, 0x6d, 0x65},
  830. {0x6d, 0x75, 0x6c, 0x74, 0x69},
  831. {0x6c, 0x69, 0x6e, 0x65},
  832. {
  833. - 0x62,
  834. + 0x42,
  835. 0x79,
  836. 0x74,
  837. ... // 2 identical elements
  838. },
  839. })),
  840. }
  841. `,
  842. }, {
  843. x: "a\nb\nc\n",
  844. y: "a\nb\nc\n",
  845. opts: []cmp.Option{
  846. cmp.Transformer("SplitLines", func(s string) []string { return strings.Split(s, "\n") }),
  847. },
  848. wantPanic: "recursive set of Transformers detected",
  849. }, {
  850. x: complex64(0),
  851. y: complex64(0),
  852. opts: []cmp.Option{
  853. cmp.Transformer("T1", func(x complex64) complex128 { return complex128(x) }),
  854. cmp.Transformer("T2", func(x complex128) [2]float64 { return [2]float64{real(x), imag(x)} }),
  855. cmp.Transformer("T3", func(x float64) complex64 { return complex64(complex(x, 0)) }),
  856. },
  857. wantPanic: "recursive set of Transformers detected",
  858. }}
  859. }
  860. func reporterTests() []test {
  861. const label = "Reporter"
  862. type (
  863. MyString string
  864. MyByte byte
  865. MyBytes []byte
  866. MyInt int8
  867. MyInts []int8
  868. MyUint int16
  869. MyUints []int16
  870. MyFloat float32
  871. MyFloats []float32
  872. MyComposite struct {
  873. StringA string
  874. StringB MyString
  875. BytesA []byte
  876. BytesB []MyByte
  877. BytesC MyBytes
  878. IntsA []int8
  879. IntsB []MyInt
  880. IntsC MyInts
  881. UintsA []uint16
  882. UintsB []MyUint
  883. UintsC MyUints
  884. FloatsA []float32
  885. FloatsB []MyFloat
  886. FloatsC MyFloats
  887. }
  888. )
  889. return []test{{
  890. label: label,
  891. x: MyComposite{IntsA: []int8{11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
  892. y: MyComposite{IntsA: []int8{10, 11, 21, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
  893. wantDiff: `
  894. cmp_test.MyComposite{
  895. ... // 3 identical fields
  896. BytesB: nil,
  897. BytesC: nil,
  898. IntsA: []int8{
  899. + 10,
  900. 11,
  901. - 12,
  902. + 21,
  903. 13,
  904. 14,
  905. ... // 15 identical elements
  906. },
  907. IntsB: nil,
  908. IntsC: nil,
  909. ... // 6 identical fields
  910. }
  911. `,
  912. reason: "unbatched diffing desired since few elements differ",
  913. }, {
  914. label: label,
  915. x: MyComposite{IntsA: []int8{10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29}},
  916. y: MyComposite{IntsA: []int8{12, 29, 13, 27, 22, 23, 17, 18, 19, 20, 21, 10, 26, 16, 25, 28, 11, 15, 24, 14}},
  917. wantDiff: `
  918. cmp_test.MyComposite{
  919. ... // 3 identical fields
  920. BytesB: nil,
  921. BytesC: nil,
  922. IntsA: []int8{
  923. - 10, 11, 12, 13, 14, 15, 16,
  924. + 12, 29, 13, 27, 22, 23,
  925. 17, 18, 19, 20, 21,
  926. - 22, 23, 24, 25, 26, 27, 28, 29,
  927. + 10, 26, 16, 25, 28, 11, 15, 24, 14,
  928. },
  929. IntsB: nil,
  930. IntsC: nil,
  931. ... // 6 identical fields
  932. }
  933. `,
  934. reason: "batched diffing desired since many elements differ",
  935. }, {
  936. label: label,
  937. x: MyComposite{
  938. BytesA: []byte{1, 2, 3},
  939. BytesB: []MyByte{4, 5, 6},
  940. BytesC: MyBytes{7, 8, 9},
  941. IntsA: []int8{-1, -2, -3},
  942. IntsB: []MyInt{-4, -5, -6},
  943. IntsC: MyInts{-7, -8, -9},
  944. UintsA: []uint16{1000, 2000, 3000},
  945. UintsB: []MyUint{4000, 5000, 6000},
  946. UintsC: MyUints{7000, 8000, 9000},
  947. FloatsA: []float32{1.5, 2.5, 3.5},
  948. FloatsB: []MyFloat{4.5, 5.5, 6.5},
  949. FloatsC: MyFloats{7.5, 8.5, 9.5},
  950. },
  951. y: MyComposite{
  952. BytesA: []byte{3, 2, 1},
  953. BytesB: []MyByte{6, 5, 4},
  954. BytesC: MyBytes{9, 8, 7},
  955. IntsA: []int8{-3, -2, -1},
  956. IntsB: []MyInt{-6, -5, -4},
  957. IntsC: MyInts{-9, -8, -7},
  958. UintsA: []uint16{3000, 2000, 1000},
  959. UintsB: []MyUint{6000, 5000, 4000},
  960. UintsC: MyUints{9000, 8000, 7000},
  961. FloatsA: []float32{3.5, 2.5, 1.5},
  962. FloatsB: []MyFloat{6.5, 5.5, 4.5},
  963. FloatsC: MyFloats{9.5, 8.5, 7.5},
  964. },
  965. wantDiff: `
  966. cmp_test.MyComposite{
  967. StringA: "",
  968. StringB: "",
  969. BytesA: []uint8{
  970. - 0x01, 0x02, 0x03, // -|...|
  971. + 0x03, 0x02, 0x01, // +|...|
  972. },
  973. BytesB: []cmp_test.MyByte{
  974. - 0x04, 0x05, 0x06,
  975. + 0x06, 0x05, 0x04,
  976. },
  977. BytesC: cmp_test.MyBytes{
  978. - 0x07, 0x08, 0x09, // -|...|
  979. + 0x09, 0x08, 0x07, // +|...|
  980. },
  981. IntsA: []int8{
  982. - -1, -2, -3,
  983. + -3, -2, -1,
  984. },
  985. IntsB: []cmp_test.MyInt{
  986. - -4, -5, -6,
  987. + -6, -5, -4,
  988. },
  989. IntsC: cmp_test.MyInts{
  990. - -7, -8, -9,
  991. + -9, -8, -7,
  992. },
  993. UintsA: []uint16{
  994. - 0x03e8, 0x07d0, 0x0bb8,
  995. + 0x0bb8, 0x07d0, 0x03e8,
  996. },
  997. UintsB: []cmp_test.MyUint{
  998. - 4000, 5000, 6000,
  999. + 6000, 5000, 4000,
  1000. },
  1001. UintsC: cmp_test.MyUints{
  1002. - 7000, 8000, 9000,
  1003. + 9000, 8000, 7000,
  1004. },
  1005. FloatsA: []float32{
  1006. - 1.5, 2.5, 3.5,
  1007. + 3.5, 2.5, 1.5,
  1008. },
  1009. FloatsB: []cmp_test.MyFloat{
  1010. - 4.5, 5.5, 6.5,
  1011. + 6.5, 5.5, 4.5,
  1012. },
  1013. FloatsC: cmp_test.MyFloats{
  1014. - 7.5, 8.5, 9.5,
  1015. + 9.5, 8.5, 7.5,
  1016. },
  1017. }
  1018. `,
  1019. reason: "batched diffing available for both named and unnamed slices",
  1020. }, {
  1021. label: label,
  1022. x: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeX\x95A\xfd$fX\x8byT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1U~{\xf6\xb3~\x1dWi \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")},
  1023. y: MyComposite{BytesA: []byte("\xf3\x0f\x8a\xa4\xd3\x12R\t$\xbeT\xac\r\xd8qwp\x20j\\s\u007f\x8c\x17U\xc04\xcen\xf7\xaaG\xee2\x9d\xc5\xca\x1eX\xaf\x8f'\xf3\x02J\x90\xedi.p2\xb4\xab0 \xb6\xbd\\b4\x17\xb0\x00\xbbO~'G\x06\xf4.f\xfdc\xd7\x04ݷ0\xb7\xd1u-[]]\xf6\xb3haha~\x1dWI \x9e\xbc\xdf\xe1M\xa9\xef\xa2\xd2\xed\xb4Gx\xc9\xc9'\xa4\xc6\xce\xecDp]")},
  1024. wantDiff: `
  1025. cmp_test.MyComposite{
  1026. StringA: "",
  1027. StringB: "",
  1028. BytesA: []uint8{
  1029. 0xf3, 0x0f, 0x8a, 0xa4, 0xd3, 0x12, 0x52, 0x09, 0x24, 0xbe, // |......R.$.|
  1030. - 0x58, 0x95, 0x41, 0xfd, 0x24, 0x66, 0x58, 0x8b, 0x79, // -|X.A.$fX.y|
  1031. 0x54, 0xac, 0x0d, 0xd8, 0x71, 0x77, 0x70, 0x20, 0x6a, 0x5c, 0x73, 0x7f, 0x8c, 0x17, 0x55, 0xc0, // |T...qwp j\s...U.|
  1032. 0x34, 0xce, 0x6e, 0xf7, 0xaa, 0x47, 0xee, 0x32, 0x9d, 0xc5, 0xca, 0x1e, 0x58, 0xaf, 0x8f, 0x27, // |4.n..G.2....X..'|
  1033. 0xf3, 0x02, 0x4a, 0x90, 0xed, 0x69, 0x2e, 0x70, 0x32, 0xb4, 0xab, 0x30, 0x20, 0xb6, 0xbd, 0x5c, // |..J..i.p2..0 ..\|
  1034. 0x62, 0x34, 0x17, 0xb0, 0x00, 0xbb, 0x4f, 0x7e, 0x27, 0x47, 0x06, 0xf4, 0x2e, 0x66, 0xfd, 0x63, // |b4....O~'G...f.c|
  1035. 0xd7, 0x04, 0xdd, 0xb7, 0x30, 0xb7, 0xd1, // |....0..|
  1036. - 0x55, 0x7e, 0x7b, 0xf6, 0xb3, 0x7e, 0x1d, 0x57, 0x69, // -|U~{..~.Wi|
  1037. + 0x75, 0x2d, 0x5b, 0x5d, 0x5d, 0xf6, 0xb3, 0x68, 0x61, 0x68, 0x61, 0x7e, 0x1d, 0x57, 0x49, // +|u-[]]..haha~.WI|
  1038. 0x20, 0x9e, 0xbc, 0xdf, 0xe1, 0x4d, 0xa9, 0xef, 0xa2, 0xd2, 0xed, 0xb4, 0x47, 0x78, 0xc9, 0xc9, // | ....M......Gx..|
  1039. 0x27, 0xa4, 0xc6, 0xce, 0xec, 0x44, 0x70, 0x5d, // |'....Dp]|
  1040. },
  1041. BytesB: nil,
  1042. BytesC: nil,
  1043. ... // 9 identical fields
  1044. }
  1045. `,
  1046. reason: "binary diff in hexdump form since data is binary data",
  1047. }, {
  1048. label: label,
  1049. x: MyComposite{StringB: MyString("readme.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000046\x0000000000000\x00011173\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
  1050. y: MyComposite{StringB: MyString("gopher.txt\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000600\x000000000\x000000000\x0000000000043\x0000000000000\x00011217\x00 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ustar\x0000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000000\x000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")},
  1051. wantDiff: `
  1052. cmp_test.MyComposite{
  1053. StringA: "",
  1054. StringB: cmp_test.MyString{
  1055. - 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, // -|readme|
  1056. + 0x67, 0x6f, 0x70, 0x68, 0x65, 0x72, // +|gopher|
  1057. 0x2e, 0x74, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |.txt............|
  1058. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................|
  1059. ... // 64 identical bytes
  1060. 0x30, 0x30, 0x36, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, // |00600.0000000.00|
  1061. 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x34, // |00000.0000000004|
  1062. - 0x36, // -|6|
  1063. + 0x33, // +|3|
  1064. 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x30, 0x31, 0x31, // |.00000000000.011|
  1065. - 0x31, 0x37, 0x33, // -|173|
  1066. + 0x32, 0x31, 0x37, // +|217|
  1067. 0x00, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |. 0.............|
  1068. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // |................|
  1069. ... // 326 identical bytes
  1070. },
  1071. BytesA: nil,
  1072. BytesB: nil,
  1073. ... // 10 identical fields
  1074. }
  1075. `,
  1076. reason: "binary diff desired since string looks like binary data",
  1077. }, {
  1078. label: label,
  1079. x: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"314 54th Avenue","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)},
  1080. y: MyComposite{BytesA: []byte(`{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"address":{"streetAddress":"21 2nd Street","city":"New York","state":"NY","postalCode":"10021-3100"},"phoneNumbers":[{"type":"home","number":"212 555-1234"},{"type":"office","number":"646 555-4567"},{"type":"mobile","number":"123 456-7890"}],"children":[],"spouse":null}`)},
  1081. wantDiff: strings.Replace(`
  1082. cmp_test.MyComposite{
  1083. StringA: "",
  1084. StringB: "",
  1085. BytesA: bytes.Join({
  1086. '{"firstName":"John","lastName":"Smith","isAlive":true,"age":27,"',
  1087. 'address":{"streetAddress":"',
  1088. - "314 54th Avenue",
  1089. + "21 2nd Street",
  1090. '","city":"New York","state":"NY","postalCode":"10021-3100"},"pho',
  1091. 'neNumbers":[{"type":"home","number":"212 555-1234"},{"type":"off',
  1092. ... // 101 identical bytes
  1093. }, ""),
  1094. BytesB: nil,
  1095. BytesC: nil,
  1096. ... // 9 identical fields
  1097. }
  1098. `, "'", "`", -1),
  1099. reason: "batched textual diff desired since bytes looks like textual data",
  1100. }, {
  1101. label: label,
  1102. x: MyComposite{
  1103. StringA: strings.TrimPrefix(`
  1104. Package cmp determines equality of values.
  1105. This package is intended to be a more powerful and safer alternative to
  1106. reflect.DeepEqual for comparing whether two values are semantically equal.
  1107. The primary features of cmp are:
  1108. • When the default behavior of equality does not suit the needs of the test,
  1109. custom equality functions can override the equality operation.
  1110. For example, an equality function may report floats as equal so long as they
  1111. are within some tolerance of each other.
  1112. • Types that have an Equal method may use that method to determine equality.
  1113. This allows package authors to determine the equality operation for the types
  1114. that they define.
  1115. • If no custom equality functions are used and no Equal method is defined,
  1116. equality is determined by recursively comparing the primitive kinds on both
  1117. values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
  1118. fields are not compared by default; they result in panics unless suppressed
  1119. by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
  1120. using the AllowUnexported option.
  1121. `, "\n"),
  1122. },
  1123. y: MyComposite{
  1124. StringA: strings.TrimPrefix(`
  1125. Package cmp determines equality of value.
  1126. This package is intended to be a more powerful and safer alternative to
  1127. reflect.DeepEqual for comparing whether two values are semantically equal.
  1128. The primary features of cmp are:
  1129. • When the default behavior of equality does not suit the needs of the test,
  1130. custom equality functions can override the equality operation.
  1131. For example, an equality function may report floats as equal so long as they
  1132. are within some tolerance of each other.
  1133. • If no custom equality functions are used and no Equal method is defined,
  1134. equality is determined by recursively comparing the primitive kinds on both
  1135. values, much like reflect.DeepEqual. Unlike reflect.DeepEqual, unexported
  1136. fields are not compared by default; they result in panics unless suppressed
  1137. by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared
  1138. using the AllowUnexported option.`, "\n"),
  1139. },
  1140. wantDiff: `
  1141. cmp_test.MyComposite{
  1142. StringA: strings.Join({
  1143. - "Package cmp determines equality of values.",
  1144. + "Package cmp determines equality of value.",
  1145. "",
  1146. "This package is intended to be a more powerful and safer alternative to",
  1147. ... // 6 identical lines
  1148. "For example, an equality function may report floats as equal so long as they",
  1149. "are within some tolerance of each other.",
  1150. - "",
  1151. - "• Types that have an Equal method may use that method to determine equality.",
  1152. - "This allows package authors to determine the equality operation for the types",
  1153. - "that they define.",
  1154. "",
  1155. "• If no custom equality functions are used and no Equal method is defined,",
  1156. ... // 3 identical lines
  1157. "by using an Ignore option (see cmpopts.IgnoreUnexported) or explicitly compared",
  1158. "using the AllowUnexported option.",
  1159. - "",
  1160. }, "\n"),
  1161. StringB: "",
  1162. BytesA: nil,
  1163. ... // 11 identical fields
  1164. }
  1165. `,
  1166. reason: "batched per-line diff desired since string looks like multi-line textual data",
  1167. }}
  1168. }
  1169. func embeddedTests() []test {
  1170. const label = "EmbeddedStruct/"
  1171. privateStruct := *new(ts.ParentStructA).PrivateStruct()
  1172. createStructA := func(i int) ts.ParentStructA {
  1173. s := ts.ParentStructA{}
  1174. s.PrivateStruct().Public = 1 + i
  1175. s.PrivateStruct().SetPrivate(2 + i)
  1176. return s
  1177. }
  1178. createStructB := func(i int) ts.ParentStructB {
  1179. s := ts.ParentStructB{}
  1180. s.PublicStruct.Public = 1 + i
  1181. s.PublicStruct.SetPrivate(2 + i)
  1182. return s
  1183. }
  1184. createStructC := func(i int) ts.ParentStructC {
  1185. s := ts.ParentStructC{}
  1186. s.PrivateStruct().Public = 1 + i
  1187. s.PrivateStruct().SetPrivate(2 + i)
  1188. s.Public = 3 + i
  1189. s.SetPrivate(4 + i)
  1190. return s
  1191. }
  1192. createStructD := func(i int) ts.ParentStructD {
  1193. s := ts.ParentStructD{}
  1194. s.PublicStruct.Public = 1 + i
  1195. s.PublicStruct.SetPrivate(2 + i)
  1196. s.Public = 3 + i
  1197. s.SetPrivate(4 + i)
  1198. return s
  1199. }
  1200. createStructE := func(i int) ts.ParentStructE {
  1201. s := ts.ParentStructE{}
  1202. s.PrivateStruct().Public = 1 + i
  1203. s.PrivateStruct().SetPrivate(2 + i)
  1204. s.PublicStruct.Public = 3 + i
  1205. s.PublicStruct.SetPrivate(4 + i)
  1206. return s
  1207. }
  1208. createStructF := func(i int) ts.ParentStructF {
  1209. s := ts.ParentStructF{}
  1210. s.PrivateStruct().Public = 1 + i
  1211. s.PrivateStruct().SetPrivate(2 + i)
  1212. s.PublicStruct.Public = 3 + i
  1213. s.PublicStruct.SetPrivate(4 + i)
  1214. s.Public = 5 + i
  1215. s.SetPrivate(6 + i)
  1216. return s
  1217. }
  1218. createStructG := func(i int) *ts.ParentStructG {
  1219. s := ts.NewParentStructG()
  1220. s.PrivateStruct().Public = 1 + i
  1221. s.PrivateStruct().SetPrivate(2 + i)
  1222. return s
  1223. }
  1224. createStructH := func(i int) *ts.ParentStructH {
  1225. s := ts.NewParentStructH()
  1226. s.PublicStruct.Public = 1 + i
  1227. s.PublicStruct.SetPrivate(2 + i)
  1228. return s
  1229. }
  1230. createStructI := func(i int) *ts.ParentStructI {
  1231. s := ts.NewParentStructI()
  1232. s.PrivateStruct().Public = 1 + i
  1233. s.PrivateStruct().SetPrivate(2 + i)
  1234. s.PublicStruct.Public = 3 + i
  1235. s.PublicStruct.SetPrivate(4 + i)
  1236. return s
  1237. }
  1238. createStructJ := func(i int) *ts.ParentStructJ {
  1239. s := ts.NewParentStructJ()
  1240. s.PrivateStruct().Public = 1 + i
  1241. s.PrivateStruct().SetPrivate(2 + i)
  1242. s.PublicStruct.Public = 3 + i
  1243. s.PublicStruct.SetPrivate(4 + i)
  1244. s.Private().Public = 5 + i
  1245. s.Private().SetPrivate(6 + i)
  1246. s.Public.Public = 7 + i
  1247. s.Public.SetPrivate(8 + i)
  1248. return s
  1249. }
  1250. // TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/21122).
  1251. wantPanicNotGo110 := func(s string) string {
  1252. if !flags.AtLeastGo110 {
  1253. return ""
  1254. }
  1255. return s
  1256. }
  1257. return []test{{
  1258. label: label + "ParentStructA",
  1259. x: ts.ParentStructA{},
  1260. y: ts.ParentStructA{},
  1261. wantPanic: "cannot handle unexported field",
  1262. }, {
  1263. label: label + "ParentStructA",
  1264. x: ts.ParentStructA{},
  1265. y: ts.ParentStructA{},
  1266. opts: []cmp.Option{
  1267. cmpopts.IgnoreUnexported(ts.ParentStructA{}),
  1268. },
  1269. }, {
  1270. label: label + "ParentStructA",
  1271. x: createStructA(0),
  1272. y: createStructA(0),
  1273. opts: []cmp.Option{
  1274. cmp.AllowUnexported(ts.ParentStructA{}),
  1275. },
  1276. wantPanic: "cannot handle unexported field",
  1277. }, {
  1278. label: label + "ParentStructA",
  1279. x: createStructA(0),
  1280. y: createStructA(0),
  1281. opts: []cmp.Option{
  1282. cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
  1283. },
  1284. }, {
  1285. label: label + "ParentStructA",
  1286. x: createStructA(0),
  1287. y: createStructA(1),
  1288. opts: []cmp.Option{
  1289. cmp.AllowUnexported(ts.ParentStructA{}, privateStruct),
  1290. },
  1291. wantDiff: `
  1292. teststructs.ParentStructA{
  1293. privateStruct: teststructs.privateStruct{
  1294. - Public: 1,
  1295. + Public: 2,
  1296. - private: 2,
  1297. + private: 3,
  1298. },
  1299. }
  1300. `,
  1301. }, {
  1302. label: label + "ParentStructB",
  1303. x: ts.ParentStructB{},
  1304. y: ts.ParentStructB{},
  1305. opts: []cmp.Option{
  1306. cmpopts.IgnoreUnexported(ts.ParentStructB{}),
  1307. },
  1308. wantPanic: "cannot handle unexported field",
  1309. }, {
  1310. label: label + "ParentStructB",
  1311. x: ts.ParentStructB{},
  1312. y: ts.ParentStructB{},
  1313. opts: []cmp.Option{
  1314. cmpopts.IgnoreUnexported(ts.ParentStructB{}),
  1315. cmpopts.IgnoreUnexported(ts.PublicStruct{}),
  1316. },
  1317. }, {
  1318. label: label + "ParentStructB",
  1319. x: createStructB(0),
  1320. y: createStructB(0),
  1321. opts: []cmp.Option{
  1322. cmp.AllowUnexported(ts.ParentStructB{}),
  1323. },
  1324. wantPanic: "cannot handle unexported field",
  1325. }, {
  1326. label: label + "ParentStructB",
  1327. x: createStructB(0),
  1328. y: createStructB(0),
  1329. opts: []cmp.Option{
  1330. cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
  1331. },
  1332. }, {
  1333. label: label + "ParentStructB",
  1334. x: createStructB(0),
  1335. y: createStructB(1),
  1336. opts: []cmp.Option{
  1337. cmp.AllowUnexported(ts.ParentStructB{}, ts.PublicStruct{}),
  1338. },
  1339. wantDiff: `
  1340. teststructs.ParentStructB{
  1341. PublicStruct: teststructs.PublicStruct{
  1342. - Public: 1,
  1343. + Public: 2,
  1344. - private: 2,
  1345. + private: 3,
  1346. },
  1347. }
  1348. `,
  1349. }, {
  1350. label: label + "ParentStructC",
  1351. x: ts.ParentStructC{},
  1352. y: ts.ParentStructC{},
  1353. wantPanic: "cannot handle unexported field",
  1354. }, {
  1355. label: label + "ParentStructC",
  1356. x: ts.ParentStructC{},
  1357. y: ts.ParentStructC{},
  1358. opts: []cmp.Option{
  1359. cmpopts.IgnoreUnexported(ts.ParentStructC{}),
  1360. },
  1361. }, {
  1362. label: label + "ParentStructC",
  1363. x: createStructC(0),
  1364. y: createStructC(0),
  1365. opts: []cmp.Option{
  1366. cmp.AllowUnexported(ts.ParentStructC{}),
  1367. },
  1368. wantPanic: "cannot handle unexported field",
  1369. }, {
  1370. label: label + "ParentStructC",
  1371. x: createStructC(0),
  1372. y: createStructC(0),
  1373. opts: []cmp.Option{
  1374. cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
  1375. },
  1376. }, {
  1377. label: label + "ParentStructC",
  1378. x: createStructC(0),
  1379. y: createStructC(1),
  1380. opts: []cmp.Option{
  1381. cmp.AllowUnexported(ts.ParentStructC{}, privateStruct),
  1382. },
  1383. wantDiff: `
  1384. teststructs.ParentStructC{
  1385. privateStruct: teststructs.privateStruct{
  1386. - Public: 1,
  1387. + Public: 2,
  1388. - private: 2,
  1389. + private: 3,
  1390. },
  1391. - Public: 3,
  1392. + Public: 4,
  1393. - private: 4,
  1394. + private: 5,
  1395. }
  1396. `,
  1397. }, {
  1398. label: label + "ParentStructD",
  1399. x: ts.ParentStructD{},
  1400. y: ts.ParentStructD{},
  1401. opts: []cmp.Option{
  1402. cmpopts.IgnoreUnexported(ts.ParentStructD{}),
  1403. },
  1404. wantPanic: "cannot handle unexported field",
  1405. }, {
  1406. label: label + "ParentStructD",
  1407. x: ts.ParentStructD{},
  1408. y: ts.ParentStructD{},
  1409. opts: []cmp.Option{
  1410. cmpopts.IgnoreUnexported(ts.ParentStructD{}),
  1411. cmpopts.IgnoreUnexported(ts.PublicStruct{}),
  1412. },
  1413. }, {
  1414. label: label + "ParentStructD",
  1415. x: createStructD(0),
  1416. y: createStructD(0),
  1417. opts: []cmp.Option{
  1418. cmp.AllowUnexported(ts.ParentStructD{}),
  1419. },
  1420. wantPanic: "cannot handle unexported field",
  1421. }, {
  1422. label: label + "ParentStructD",
  1423. x: createStructD(0),
  1424. y: createStructD(0),
  1425. opts: []cmp.Option{
  1426. cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
  1427. },
  1428. }, {
  1429. label: label + "ParentStructD",
  1430. x: createStructD(0),
  1431. y: createStructD(1),
  1432. opts: []cmp.Option{
  1433. cmp.AllowUnexported(ts.ParentStructD{}, ts.PublicStruct{}),
  1434. },
  1435. wantDiff: `
  1436. teststructs.ParentStructD{
  1437. PublicStruct: teststructs.PublicStruct{
  1438. - Public: 1,
  1439. + Public: 2,
  1440. - private: 2,
  1441. + private: 3,
  1442. },
  1443. - Public: 3,
  1444. + Public: 4,
  1445. - private: 4,
  1446. + private: 5,
  1447. }
  1448. `,
  1449. }, {
  1450. label: label + "ParentStructE",
  1451. x: ts.ParentStructE{},
  1452. y: ts.ParentStructE{},
  1453. opts: []cmp.Option{
  1454. cmpopts.IgnoreUnexported(ts.ParentStructE{}),
  1455. },
  1456. wantPanic: "cannot handle unexported field",
  1457. }, {
  1458. label: label + "ParentStructE",
  1459. x: ts.ParentStructE{},
  1460. y: ts.ParentStructE{},
  1461. opts: []cmp.Option{
  1462. cmpopts.IgnoreUnexported(ts.ParentStructE{}),
  1463. cmpopts.IgnoreUnexported(ts.PublicStruct{}),
  1464. },
  1465. }, {
  1466. label: label + "ParentStructE",
  1467. x: createStructE(0),
  1468. y: createStructE(0),
  1469. opts: []cmp.Option{
  1470. cmp.AllowUnexported(ts.ParentStructE{}),
  1471. },
  1472. wantPanic: "cannot handle unexported field",
  1473. }, {
  1474. label: label + "ParentStructE",
  1475. x: createStructE(0),
  1476. y: createStructE(0),
  1477. opts: []cmp.Option{
  1478. cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}),
  1479. },
  1480. wantPanic: "cannot handle unexported field",
  1481. }, {
  1482. label: label + "ParentStructE",
  1483. x: createStructE(0),
  1484. y: createStructE(0),
  1485. opts: []cmp.Option{
  1486. cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
  1487. },
  1488. }, {
  1489. label: label + "ParentStructE",
  1490. x: createStructE(0),
  1491. y: createStructE(1),
  1492. opts: []cmp.Option{
  1493. cmp.AllowUnexported(ts.ParentStructE{}, ts.PublicStruct{}, privateStruct),
  1494. },
  1495. wantDiff: `
  1496. teststructs.ParentStructE{
  1497. privateStruct: teststructs.privateStruct{
  1498. - Public: 1,
  1499. + Public: 2,
  1500. - private: 2,
  1501. + private: 3,
  1502. },
  1503. PublicStruct: teststructs.PublicStruct{
  1504. - Public: 3,
  1505. + Public: 4,
  1506. - private: 4,
  1507. + private: 5,
  1508. },
  1509. }
  1510. `,
  1511. }, {
  1512. label: label + "ParentStructF",
  1513. x: ts.ParentStructF{},
  1514. y: ts.ParentStructF{},
  1515. opts: []cmp.Option{
  1516. cmpopts.IgnoreUnexported(ts.ParentStructF{}),
  1517. },
  1518. wantPanic: "cannot handle unexported field",
  1519. }, {
  1520. label: label + "ParentStructF",
  1521. x: ts.ParentStructF{},
  1522. y: ts.ParentStructF{},
  1523. opts: []cmp.Option{
  1524. cmpopts.IgnoreUnexported(ts.ParentStructF{}),
  1525. cmpopts.IgnoreUnexported(ts.PublicStruct{}),
  1526. },
  1527. }, {
  1528. label: label + "ParentStructF",
  1529. x: createStructF(0),
  1530. y: createStructF(0),
  1531. opts: []cmp.Option{
  1532. cmp.AllowUnexported(ts.ParentStructF{}),
  1533. },
  1534. wantPanic: "cannot handle unexported field",
  1535. }, {
  1536. label: label + "ParentStructF",
  1537. x: createStructF(0),
  1538. y: createStructF(0),
  1539. opts: []cmp.Option{
  1540. cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}),
  1541. },
  1542. wantPanic: "cannot handle unexported field",
  1543. }, {
  1544. label: label + "ParentStructF",
  1545. x: createStructF(0),
  1546. y: createStructF(0),
  1547. opts: []cmp.Option{
  1548. cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
  1549. },
  1550. }, {
  1551. label: label + "ParentStructF",
  1552. x: createStructF(0),
  1553. y: createStructF(1),
  1554. opts: []cmp.Option{
  1555. cmp.AllowUnexported(ts.ParentStructF{}, ts.PublicStruct{}, privateStruct),
  1556. },
  1557. wantDiff: `
  1558. teststructs.ParentStructF{
  1559. privateStruct: teststructs.privateStruct{
  1560. - Public: 1,
  1561. + Public: 2,
  1562. - private: 2,
  1563. + private: 3,
  1564. },
  1565. PublicStruct: teststructs.PublicStruct{
  1566. - Public: 3,
  1567. + Public: 4,
  1568. - private: 4,
  1569. + private: 5,
  1570. },
  1571. - Public: 5,
  1572. + Public: 6,
  1573. - private: 6,
  1574. + private: 7,
  1575. }
  1576. `,
  1577. }, {
  1578. label: label + "ParentStructG",
  1579. x: ts.ParentStructG{},
  1580. y: ts.ParentStructG{},
  1581. wantPanic: wantPanicNotGo110("cannot handle unexported field"),
  1582. }, {
  1583. label: label + "ParentStructG",
  1584. x: ts.ParentStructG{},
  1585. y: ts.ParentStructG{},
  1586. opts: []cmp.Option{
  1587. cmpopts.IgnoreUnexported(ts.ParentStructG{}),
  1588. },
  1589. }, {
  1590. label: label + "ParentStructG",
  1591. x: createStructG(0),
  1592. y: createStructG(0),
  1593. opts: []cmp.Option{
  1594. cmp.AllowUnexported(ts.ParentStructG{}),
  1595. },
  1596. wantPanic: "cannot handle unexported field",
  1597. }, {
  1598. label: label + "ParentStructG",
  1599. x: createStructG(0),
  1600. y: createStructG(0),
  1601. opts: []cmp.Option{
  1602. cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
  1603. },
  1604. }, {
  1605. label: label + "ParentStructG",
  1606. x: createStructG(0),
  1607. y: createStructG(1),
  1608. opts: []cmp.Option{
  1609. cmp.AllowUnexported(ts.ParentStructG{}, privateStruct),
  1610. },
  1611. wantDiff: `
  1612. &teststructs.ParentStructG{
  1613. privateStruct: &teststructs.privateStruct{
  1614. - Public: 1,
  1615. + Public: 2,
  1616. - private: 2,
  1617. + private: 3,
  1618. },
  1619. }
  1620. `,
  1621. }, {
  1622. label: label + "ParentStructH",
  1623. x: ts.ParentStructH{},
  1624. y: ts.ParentStructH{},
  1625. }, {
  1626. label: label + "ParentStructH",
  1627. x: createStructH(0),
  1628. y: createStructH(0),
  1629. wantPanic: "cannot handle unexported field",
  1630. }, {
  1631. label: label + "ParentStructH",
  1632. x: ts.ParentStructH{},
  1633. y: ts.ParentStructH{},
  1634. opts: []cmp.Option{
  1635. cmpopts.IgnoreUnexported(ts.ParentStructH{}),
  1636. },
  1637. }, {
  1638. label: label + "ParentStructH",
  1639. x: createStructH(0),
  1640. y: createStructH(0),
  1641. opts: []cmp.Option{
  1642. cmp.AllowUnexported(ts.ParentStructH{}),
  1643. },
  1644. wantPanic: "cannot handle unexported field",
  1645. }, {
  1646. label: label + "ParentStructH",
  1647. x: createStructH(0),
  1648. y: createStructH(0),
  1649. opts: []cmp.Option{
  1650. cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
  1651. },
  1652. }, {
  1653. label: label + "ParentStructH",
  1654. x: createStructH(0),
  1655. y: createStructH(1),
  1656. opts: []cmp.Option{
  1657. cmp.AllowUnexported(ts.ParentStructH{}, ts.PublicStruct{}),
  1658. },
  1659. wantDiff: `
  1660. &teststructs.ParentStructH{
  1661. PublicStruct: &teststructs.PublicStruct{
  1662. - Public: 1,
  1663. + Public: 2,
  1664. - private: 2,
  1665. + private: 3,
  1666. },
  1667. }
  1668. `,
  1669. }, {
  1670. label: label + "ParentStructI",
  1671. x: ts.ParentStructI{},
  1672. y: ts.ParentStructI{},
  1673. wantPanic: wantPanicNotGo110("cannot handle unexported field"),
  1674. }, {
  1675. label: label + "ParentStructI",
  1676. x: ts.ParentStructI{},
  1677. y: ts.ParentStructI{},
  1678. opts: []cmp.Option{
  1679. cmpopts.IgnoreUnexported(ts.ParentStructI{}),
  1680. },
  1681. }, {
  1682. label: label + "ParentStructI",
  1683. x: createStructI(0),
  1684. y: createStructI(0),
  1685. opts: []cmp.Option{
  1686. cmpopts.IgnoreUnexported(ts.ParentStructI{}),
  1687. },
  1688. wantPanic: "cannot handle unexported field",
  1689. }, {
  1690. label: label + "ParentStructI",
  1691. x: createStructI(0),
  1692. y: createStructI(0),
  1693. opts: []cmp.Option{
  1694. cmpopts.IgnoreUnexported(ts.ParentStructI{}, ts.PublicStruct{}),
  1695. },
  1696. }, {
  1697. label: label + "ParentStructI",
  1698. x: createStructI(0),
  1699. y: createStructI(0),
  1700. opts: []cmp.Option{
  1701. cmp.AllowUnexported(ts.ParentStructI{}),
  1702. },
  1703. wantPanic: "cannot handle unexported field",
  1704. }, {
  1705. label: label + "ParentStructI",
  1706. x: createStructI(0),
  1707. y: createStructI(0),
  1708. opts: []cmp.Option{
  1709. cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
  1710. },
  1711. }, {
  1712. label: label + "ParentStructI",
  1713. x: createStructI(0),
  1714. y: createStructI(1),
  1715. opts: []cmp.Option{
  1716. cmp.AllowUnexported(ts.ParentStructI{}, ts.PublicStruct{}, privateStruct),
  1717. },
  1718. wantDiff: `
  1719. &teststructs.ParentStructI{
  1720. privateStruct: &teststructs.privateStruct{
  1721. - Public: 1,
  1722. + Public: 2,
  1723. - private: 2,
  1724. + private: 3,
  1725. },
  1726. PublicStruct: &teststructs.PublicStruct{
  1727. - Public: 3,
  1728. + Public: 4,
  1729. - private: 4,
  1730. + private: 5,
  1731. },
  1732. }
  1733. `,
  1734. }, {
  1735. label: label + "ParentStructJ",
  1736. x: ts.ParentStructJ{},
  1737. y: ts.ParentStructJ{},
  1738. wantPanic: "cannot handle unexported field",
  1739. }, {
  1740. label: label + "ParentStructJ",
  1741. x: ts.ParentStructJ{},
  1742. y: ts.ParentStructJ{},
  1743. opts: []cmp.Option{
  1744. cmpopts.IgnoreUnexported(ts.ParentStructJ{}),
  1745. },
  1746. wantPanic: "cannot handle unexported field",
  1747. }, {
  1748. label: label + "ParentStructJ",
  1749. x: ts.ParentStructJ{},
  1750. y: ts.ParentStructJ{},
  1751. opts: []cmp.Option{
  1752. cmpopts.IgnoreUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
  1753. },
  1754. }, {
  1755. label: label + "ParentStructJ",
  1756. x: createStructJ(0),
  1757. y: createStructJ(0),
  1758. opts: []cmp.Option{
  1759. cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}),
  1760. },
  1761. wantPanic: "cannot handle unexported field",
  1762. }, {
  1763. label: label + "ParentStructJ",
  1764. x: createStructJ(0),
  1765. y: createStructJ(0),
  1766. opts: []cmp.Option{
  1767. cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
  1768. },
  1769. }, {
  1770. label: label + "ParentStructJ",
  1771. x: createStructJ(0),
  1772. y: createStructJ(1),
  1773. opts: []cmp.Option{
  1774. cmp.AllowUnexported(ts.ParentStructJ{}, ts.PublicStruct{}, privateStruct),
  1775. },
  1776. wantDiff: `
  1777. &teststructs.ParentStructJ{
  1778. privateStruct: &teststructs.privateStruct{
  1779. - Public: 1,
  1780. + Public: 2,
  1781. - private: 2,
  1782. + private: 3,
  1783. },
  1784. PublicStruct: &teststructs.PublicStruct{
  1785. - Public: 3,
  1786. + Public: 4,
  1787. - private: 4,
  1788. + private: 5,
  1789. },
  1790. Public: teststructs.PublicStruct{
  1791. - Public: 7,
  1792. + Public: 8,
  1793. - private: 8,
  1794. + private: 9,
  1795. },
  1796. private: teststructs.privateStruct{
  1797. - Public: 5,
  1798. + Public: 6,
  1799. - private: 6,
  1800. + private: 7,
  1801. },
  1802. }
  1803. `,
  1804. }}
  1805. }
  1806. func methodTests() []test {
  1807. const label = "EqualMethod/"
  1808. // A common mistake that the Equal method is on a pointer receiver,
  1809. // but only a non-pointer value is present in the struct.
  1810. // A transform can be used to forcibly reference the value.
  1811. derefTransform := cmp.FilterPath(func(p cmp.Path) bool {
  1812. if len(p) == 0 {
  1813. return false
  1814. }
  1815. t := p[len(p)-1].Type()
  1816. if _, ok := t.MethodByName("Equal"); ok || t.Kind() == reflect.Ptr {
  1817. return false
  1818. }
  1819. if m, ok := reflect.PtrTo(t).MethodByName("Equal"); ok {
  1820. tf := m.Func.Type()
  1821. return !tf.IsVariadic() && tf.NumIn() == 2 && tf.NumOut() == 1 &&
  1822. tf.In(0).AssignableTo(tf.In(1)) && tf.Out(0) == reflect.TypeOf(true)
  1823. }
  1824. return false
  1825. }, cmp.Transformer("Ref", func(x interface{}) interface{} {
  1826. v := reflect.ValueOf(x)
  1827. vp := reflect.New(v.Type())
  1828. vp.Elem().Set(v)
  1829. return vp.Interface()
  1830. }))
  1831. // For each of these types, there is an Equal method defined, which always
  1832. // returns true, while the underlying data are fundamentally different.
  1833. // Since the method should be called, these are expected to be equal.
  1834. return []test{{
  1835. label: label + "StructA",
  1836. x: ts.StructA{X: "NotEqual"},
  1837. y: ts.StructA{X: "not_equal"},
  1838. }, {
  1839. label: label + "StructA",
  1840. x: &ts.StructA{X: "NotEqual"},
  1841. y: &ts.StructA{X: "not_equal"},
  1842. }, {
  1843. label: label + "StructB",
  1844. x: ts.StructB{X: "NotEqual"},
  1845. y: ts.StructB{X: "not_equal"},
  1846. wantDiff: `
  1847. teststructs.StructB{
  1848. - X: "NotEqual",
  1849. + X: "not_equal",
  1850. }
  1851. `,
  1852. }, {
  1853. label: label + "StructB",
  1854. x: ts.StructB{X: "NotEqual"},
  1855. y: ts.StructB{X: "not_equal"},
  1856. opts: []cmp.Option{derefTransform},
  1857. }, {
  1858. label: label + "StructB",
  1859. x: &ts.StructB{X: "NotEqual"},
  1860. y: &ts.StructB{X: "not_equal"},
  1861. }, {
  1862. label: label + "StructC",
  1863. x: ts.StructC{X: "NotEqual"},
  1864. y: ts.StructC{X: "not_equal"},
  1865. }, {
  1866. label: label + "StructC",
  1867. x: &ts.StructC{X: "NotEqual"},
  1868. y: &ts.StructC{X: "not_equal"},
  1869. }, {
  1870. label: label + "StructD",
  1871. x: ts.StructD{X: "NotEqual"},
  1872. y: ts.StructD{X: "not_equal"},
  1873. wantDiff: `
  1874. teststructs.StructD{
  1875. - X: "NotEqual",
  1876. + X: "not_equal",
  1877. }
  1878. `,
  1879. }, {
  1880. label: label + "StructD",
  1881. x: ts.StructD{X: "NotEqual"},
  1882. y: ts.StructD{X: "not_equal"},
  1883. opts: []cmp.Option{derefTransform},
  1884. }, {
  1885. label: label + "StructD",
  1886. x: &ts.StructD{X: "NotEqual"},
  1887. y: &ts.StructD{X: "not_equal"},
  1888. }, {
  1889. label: label + "StructE",
  1890. x: ts.StructE{X: "NotEqual"},
  1891. y: ts.StructE{X: "not_equal"},
  1892. wantDiff: `
  1893. teststructs.StructE{
  1894. - X: "NotEqual",
  1895. + X: "not_equal",
  1896. }
  1897. `,
  1898. }, {
  1899. label: label + "StructE",
  1900. x: ts.StructE{X: "NotEqual"},
  1901. y: ts.StructE{X: "not_equal"},
  1902. opts: []cmp.Option{derefTransform},
  1903. }, {
  1904. label: label + "StructE",
  1905. x: &ts.StructE{X: "NotEqual"},
  1906. y: &ts.StructE{X: "not_equal"},
  1907. }, {
  1908. label: label + "StructF",
  1909. x: ts.StructF{X: "NotEqual"},
  1910. y: ts.StructF{X: "not_equal"},
  1911. wantDiff: `
  1912. teststructs.StructF{
  1913. - X: "NotEqual",
  1914. + X: "not_equal",
  1915. }
  1916. `,
  1917. }, {
  1918. label: label + "StructF",
  1919. x: &ts.StructF{X: "NotEqual"},
  1920. y: &ts.StructF{X: "not_equal"},
  1921. }, {
  1922. label: label + "StructA1",
  1923. x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
  1924. y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
  1925. }, {
  1926. label: label + "StructA1",
  1927. x: ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
  1928. y: ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
  1929. wantDiff: `
  1930. teststructs.StructA1{
  1931. StructA: teststructs.StructA{X: "NotEqual"},
  1932. - X: "NotEqual",
  1933. + X: "not_equal",
  1934. }
  1935. `,
  1936. }, {
  1937. label: label + "StructA1",
  1938. x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "equal"},
  1939. y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "equal"},
  1940. }, {
  1941. label: label + "StructA1",
  1942. x: &ts.StructA1{StructA: ts.StructA{X: "NotEqual"}, X: "NotEqual"},
  1943. y: &ts.StructA1{StructA: ts.StructA{X: "not_equal"}, X: "not_equal"},
  1944. wantDiff: `
  1945. &teststructs.StructA1{
  1946. StructA: teststructs.StructA{X: "NotEqual"},
  1947. - X: "NotEqual",
  1948. + X: "not_equal",
  1949. }
  1950. `,
  1951. }, {
  1952. label: label + "StructB1",
  1953. x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
  1954. y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
  1955. opts: []cmp.Option{derefTransform},
  1956. }, {
  1957. label: label + "StructB1",
  1958. x: ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
  1959. y: ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
  1960. opts: []cmp.Option{derefTransform},
  1961. wantDiff: `
  1962. teststructs.StructB1{
  1963. StructB: teststructs.StructB(Inverse(Ref, &teststructs.StructB{X: "NotEqual"})),
  1964. - X: "NotEqual",
  1965. + X: "not_equal",
  1966. }
  1967. `,
  1968. }, {
  1969. label: label + "StructB1",
  1970. x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "equal"},
  1971. y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "equal"},
  1972. opts: []cmp.Option{derefTransform},
  1973. }, {
  1974. label: label + "StructB1",
  1975. x: &ts.StructB1{StructB: ts.StructB{X: "NotEqual"}, X: "NotEqual"},
  1976. y: &ts.StructB1{StructB: ts.StructB{X: "not_equal"}, X: "not_equal"},
  1977. opts: []cmp.Option{derefTransform},
  1978. wantDiff: `
  1979. &teststructs.StructB1{
  1980. StructB: teststructs.StructB(Inverse(Ref, &teststructs.StructB{X: "NotEqual"})),
  1981. - X: "NotEqual",
  1982. + X: "not_equal",
  1983. }
  1984. `,
  1985. }, {
  1986. label: label + "StructC1",
  1987. x: ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
  1988. y: ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
  1989. }, {
  1990. label: label + "StructC1",
  1991. x: &ts.StructC1{StructC: ts.StructC{X: "NotEqual"}, X: "NotEqual"},
  1992. y: &ts.StructC1{StructC: ts.StructC{X: "not_equal"}, X: "not_equal"},
  1993. }, {
  1994. label: label + "StructD1",
  1995. x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
  1996. y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
  1997. wantDiff: `
  1998. teststructs.StructD1{
  1999. - StructD: teststructs.StructD{X: "NotEqual"},
  2000. + StructD: teststructs.StructD{X: "not_equal"},
  2001. - X: "NotEqual",
  2002. + X: "not_equal",
  2003. }
  2004. `,
  2005. }, {
  2006. label: label + "StructD1",
  2007. x: ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
  2008. y: ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
  2009. opts: []cmp.Option{derefTransform},
  2010. }, {
  2011. label: label + "StructD1",
  2012. x: &ts.StructD1{StructD: ts.StructD{X: "NotEqual"}, X: "NotEqual"},
  2013. y: &ts.StructD1{StructD: ts.StructD{X: "not_equal"}, X: "not_equal"},
  2014. }, {
  2015. label: label + "StructE1",
  2016. x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
  2017. y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
  2018. wantDiff: `
  2019. teststructs.StructE1{
  2020. - StructE: teststructs.StructE{X: "NotEqual"},
  2021. + StructE: teststructs.StructE{X: "not_equal"},
  2022. - X: "NotEqual",
  2023. + X: "not_equal",
  2024. }
  2025. `,
  2026. }, {
  2027. label: label + "StructE1",
  2028. x: ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
  2029. y: ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
  2030. opts: []cmp.Option{derefTransform},
  2031. }, {
  2032. label: label + "StructE1",
  2033. x: &ts.StructE1{StructE: ts.StructE{X: "NotEqual"}, X: "NotEqual"},
  2034. y: &ts.StructE1{StructE: ts.StructE{X: "not_equal"}, X: "not_equal"},
  2035. }, {
  2036. label: label + "StructF1",
  2037. x: ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
  2038. y: ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
  2039. wantDiff: `
  2040. teststructs.StructF1{
  2041. - StructF: teststructs.StructF{X: "NotEqual"},
  2042. + StructF: teststructs.StructF{X: "not_equal"},
  2043. - X: "NotEqual",
  2044. + X: "not_equal",
  2045. }
  2046. `,
  2047. }, {
  2048. label: label + "StructF1",
  2049. x: &ts.StructF1{StructF: ts.StructF{X: "NotEqual"}, X: "NotEqual"},
  2050. y: &ts.StructF1{StructF: ts.StructF{X: "not_equal"}, X: "not_equal"},
  2051. }, {
  2052. label: label + "StructA2",
  2053. x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
  2054. y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
  2055. }, {
  2056. label: label + "StructA2",
  2057. x: ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
  2058. y: ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
  2059. wantDiff: `
  2060. teststructs.StructA2{
  2061. StructA: &teststructs.StructA{X: "NotEqual"},
  2062. - X: "NotEqual",
  2063. + X: "not_equal",
  2064. }
  2065. `,
  2066. }, {
  2067. label: label + "StructA2",
  2068. x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "equal"},
  2069. y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "equal"},
  2070. }, {
  2071. label: label + "StructA2",
  2072. x: &ts.StructA2{StructA: &ts.StructA{X: "NotEqual"}, X: "NotEqual"},
  2073. y: &ts.StructA2{StructA: &ts.StructA{X: "not_equal"}, X: "not_equal"},
  2074. wantDiff: `
  2075. &teststructs.StructA2{
  2076. StructA: &teststructs.StructA{X: "NotEqual"},
  2077. - X: "NotEqual",
  2078. + X: "not_equal",
  2079. }
  2080. `,
  2081. }, {
  2082. label: label + "StructB2",
  2083. x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
  2084. y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
  2085. }, {
  2086. label: label + "StructB2",
  2087. x: ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
  2088. y: ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
  2089. wantDiff: `
  2090. teststructs.StructB2{
  2091. StructB: &teststructs.StructB{X: "NotEqual"},
  2092. - X: "NotEqual",
  2093. + X: "not_equal",
  2094. }
  2095. `,
  2096. }, {
  2097. label: label + "StructB2",
  2098. x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "equal"},
  2099. y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "equal"},
  2100. }, {
  2101. label: label + "StructB2",
  2102. x: &ts.StructB2{StructB: &ts.StructB{X: "NotEqual"}, X: "NotEqual"},
  2103. y: &ts.StructB2{StructB: &ts.StructB{X: "not_equal"}, X: "not_equal"},
  2104. wantDiff: `
  2105. &teststructs.StructB2{
  2106. StructB: &teststructs.StructB{X: "NotEqual"},
  2107. - X: "NotEqual",
  2108. + X: "not_equal",
  2109. }
  2110. `,
  2111. }, {
  2112. label: label + "StructC2",
  2113. x: ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
  2114. y: ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
  2115. }, {
  2116. label: label + "StructC2",
  2117. x: &ts.StructC2{StructC: &ts.StructC{X: "NotEqual"}, X: "NotEqual"},
  2118. y: &ts.StructC2{StructC: &ts.StructC{X: "not_equal"}, X: "not_equal"},
  2119. }, {
  2120. label: label + "StructD2",
  2121. x: ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
  2122. y: ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
  2123. }, {
  2124. label: label + "StructD2",
  2125. x: &ts.StructD2{StructD: &ts.StructD{X: "NotEqual"}, X: "NotEqual"},
  2126. y: &ts.StructD2{StructD: &ts.StructD{X: "not_equal"}, X: "not_equal"},
  2127. }, {
  2128. label: label + "StructE2",
  2129. x: ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
  2130. y: ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
  2131. }, {
  2132. label: label + "StructE2",
  2133. x: &ts.StructE2{StructE: &ts.StructE{X: "NotEqual"}, X: "NotEqual"},
  2134. y: &ts.StructE2{StructE: &ts.StructE{X: "not_equal"}, X: "not_equal"},
  2135. }, {
  2136. label: label + "StructF2",
  2137. x: ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
  2138. y: ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
  2139. }, {
  2140. label: label + "StructF2",
  2141. x: &ts.StructF2{StructF: &ts.StructF{X: "NotEqual"}, X: "NotEqual"},
  2142. y: &ts.StructF2{StructF: &ts.StructF{X: "not_equal"}, X: "not_equal"},
  2143. }, {
  2144. label: label + "StructNo",
  2145. x: ts.StructNo{X: "NotEqual"},
  2146. y: ts.StructNo{X: "not_equal"},
  2147. wantDiff: `
  2148. teststructs.StructNo{
  2149. - X: "NotEqual",
  2150. + X: "not_equal",
  2151. }
  2152. `,
  2153. }, {
  2154. label: label + "AssignA",
  2155. x: ts.AssignA(func() int { return 0 }),
  2156. y: ts.AssignA(func() int { return 1 }),
  2157. }, {
  2158. label: label + "AssignB",
  2159. x: ts.AssignB(struct{ A int }{0}),
  2160. y: ts.AssignB(struct{ A int }{1}),
  2161. }, {
  2162. label: label + "AssignC",
  2163. x: ts.AssignC(make(chan bool)),
  2164. y: ts.AssignC(make(chan bool)),
  2165. }, {
  2166. label: label + "AssignD",
  2167. x: ts.AssignD(make(chan bool)),
  2168. y: ts.AssignD(make(chan bool)),
  2169. }}
  2170. }
  2171. func project1Tests() []test {
  2172. const label = "Project1"
  2173. ignoreUnexported := cmpopts.IgnoreUnexported(
  2174. ts.EagleImmutable{},
  2175. ts.DreamerImmutable{},
  2176. ts.SlapImmutable{},
  2177. ts.GoatImmutable{},
  2178. ts.DonkeyImmutable{},
  2179. ts.LoveRadius{},
  2180. ts.SummerLove{},
  2181. ts.SummerLoveSummary{},
  2182. )
  2183. createEagle := func() ts.Eagle {
  2184. return ts.Eagle{
  2185. Name: "eagle",
  2186. Hounds: []string{"buford", "tannen"},
  2187. Desc: "some description",
  2188. Dreamers: []ts.Dreamer{{}, {
  2189. Name: "dreamer2",
  2190. Animal: []interface{}{
  2191. ts.Goat{
  2192. Target: "corporation",
  2193. Immutable: &ts.GoatImmutable{
  2194. ID: "southbay",
  2195. State: (*pb.Goat_States)(intPtr(5)),
  2196. Started: now,
  2197. },
  2198. },
  2199. ts.Donkey{},
  2200. },
  2201. Amoeba: 53,
  2202. }},
  2203. Slaps: []ts.Slap{{
  2204. Name: "slapID",
  2205. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
  2206. Immutable: &ts.SlapImmutable{
  2207. ID: "immutableSlap",
  2208. MildSlap: true,
  2209. Started: now,
  2210. LoveRadius: &ts.LoveRadius{
  2211. Summer: &ts.SummerLove{
  2212. Summary: &ts.SummerLoveSummary{
  2213. Devices: []string{"foo", "bar", "baz"},
  2214. ChangeType: []pb.SummerType{1, 2, 3},
  2215. },
  2216. },
  2217. },
  2218. },
  2219. }},
  2220. Immutable: &ts.EagleImmutable{
  2221. ID: "eagleID",
  2222. Birthday: now,
  2223. MissingCall: (*pb.Eagle_MissingCalls)(intPtr(55)),
  2224. },
  2225. }
  2226. }
  2227. return []test{{
  2228. label: label,
  2229. x: ts.Eagle{Slaps: []ts.Slap{{
  2230. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
  2231. }}},
  2232. y: ts.Eagle{Slaps: []ts.Slap{{
  2233. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
  2234. }}},
  2235. wantPanic: "cannot handle unexported field",
  2236. }, {
  2237. label: label,
  2238. x: ts.Eagle{Slaps: []ts.Slap{{
  2239. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
  2240. }}},
  2241. y: ts.Eagle{Slaps: []ts.Slap{{
  2242. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
  2243. }}},
  2244. opts: []cmp.Option{cmp.Comparer(pb.Equal)},
  2245. }, {
  2246. label: label,
  2247. x: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
  2248. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata"}},
  2249. }}},
  2250. y: ts.Eagle{Slaps: []ts.Slap{{}, {}, {}, {}, {
  2251. Args: &pb.MetaData{Stringer: pb.Stringer{X: "metadata2"}},
  2252. }}},
  2253. opts: []cmp.Option{cmp.Comparer(pb.Equal)},
  2254. wantDiff: `
  2255. teststructs.Eagle{
  2256. ... // 4 identical fields
  2257. Dreamers: nil,
  2258. Prong: 0,
  2259. Slaps: []teststructs.Slap{
  2260. ... // 2 identical elements
  2261. {},
  2262. {},
  2263. {
  2264. Name: "",
  2265. Desc: "",
  2266. DescLong: "",
  2267. - Args: s"metadata",
  2268. + Args: s"metadata2",
  2269. Tense: 0,
  2270. Interval: 0,
  2271. ... // 3 identical fields
  2272. },
  2273. },
  2274. StateGoverner: "",
  2275. PrankRating: "",
  2276. ... // 2 identical fields
  2277. }
  2278. `,
  2279. }, {
  2280. label: label,
  2281. x: createEagle(),
  2282. y: createEagle(),
  2283. opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
  2284. }, {
  2285. label: label,
  2286. x: func() ts.Eagle {
  2287. eg := createEagle()
  2288. eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.ID = "southbay2"
  2289. eg.Dreamers[1].Animal[0].(ts.Goat).Immutable.State = (*pb.Goat_States)(intPtr(6))
  2290. eg.Slaps[0].Immutable.MildSlap = false
  2291. return eg
  2292. }(),
  2293. y: func() ts.Eagle {
  2294. eg := createEagle()
  2295. devs := eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices
  2296. eg.Slaps[0].Immutable.LoveRadius.Summer.Summary.Devices = devs[:1]
  2297. return eg
  2298. }(),
  2299. opts: []cmp.Option{ignoreUnexported, cmp.Comparer(pb.Equal)},
  2300. wantDiff: `
  2301. teststructs.Eagle{
  2302. ... // 2 identical fields
  2303. Desc: "some description",
  2304. DescLong: "",
  2305. Dreamers: []teststructs.Dreamer{
  2306. {},
  2307. {
  2308. ... // 4 identical fields
  2309. ContSlaps: nil,
  2310. ContSlapsInterval: 0,
  2311. Animal: []interface{}{
  2312. teststructs.Goat{
  2313. Target: "corporation",
  2314. Slaps: nil,
  2315. FunnyPrank: "",
  2316. Immutable: &teststructs.GoatImmutable{
  2317. - ID: "southbay2",
  2318. + ID: "southbay",
  2319. - State: &6,
  2320. + State: &5,
  2321. Started: s"2009-11-10 23:00:00 +0000 UTC",
  2322. Stopped: s"0001-01-01 00:00:00 +0000 UTC",
  2323. ... // 1 ignored and 1 identical fields
  2324. },
  2325. },
  2326. teststructs.Donkey{},
  2327. },
  2328. Ornamental: false,
  2329. Amoeba: 53,
  2330. ... // 5 identical fields
  2331. },
  2332. },
  2333. Prong: 0,
  2334. Slaps: []teststructs.Slap{
  2335. {
  2336. ... // 6 identical fields
  2337. Homeland: 0x00,
  2338. FunnyPrank: "",
  2339. Immutable: &teststructs.SlapImmutable{
  2340. ID: "immutableSlap",
  2341. Out: nil,
  2342. - MildSlap: false,
  2343. + MildSlap: true,
  2344. PrettyPrint: "",
  2345. State: nil,
  2346. Started: s"2009-11-10 23:00:00 +0000 UTC",
  2347. Stopped: s"0001-01-01 00:00:00 +0000 UTC",
  2348. LastUpdate: s"0001-01-01 00:00:00 +0000 UTC",
  2349. LoveRadius: &teststructs.LoveRadius{
  2350. Summer: &teststructs.SummerLove{
  2351. Summary: &teststructs.SummerLoveSummary{
  2352. Devices: []string{
  2353. "foo",
  2354. - "bar",
  2355. - "baz",
  2356. },
  2357. ChangeType: []testprotos.SummerType{1, 2, 3},
  2358. ... // 1 ignored field
  2359. },
  2360. ... // 1 ignored field
  2361. },
  2362. ... // 1 ignored field
  2363. },
  2364. ... // 1 ignored field
  2365. },
  2366. },
  2367. },
  2368. StateGoverner: "",
  2369. PrankRating: "",
  2370. ... // 2 identical fields
  2371. }
  2372. `,
  2373. }}
  2374. }
  2375. type germSorter []*pb.Germ
  2376. func (gs germSorter) Len() int { return len(gs) }
  2377. func (gs germSorter) Less(i, j int) bool { return gs[i].String() < gs[j].String() }
  2378. func (gs germSorter) Swap(i, j int) { gs[i], gs[j] = gs[j], gs[i] }
  2379. func project2Tests() []test {
  2380. const label = "Project2"
  2381. sortGerms := cmp.Transformer("Sort", func(in []*pb.Germ) []*pb.Germ {
  2382. out := append([]*pb.Germ(nil), in...) // Make copy
  2383. sort.Sort(germSorter(out))
  2384. return out
  2385. })
  2386. equalDish := cmp.Comparer(func(x, y *ts.Dish) bool {
  2387. if x == nil || y == nil {
  2388. return x == nil && y == nil
  2389. }
  2390. px, err1 := x.Proto()
  2391. py, err2 := y.Proto()
  2392. if err1 != nil || err2 != nil {
  2393. return err1 == err2
  2394. }
  2395. return pb.Equal(px, py)
  2396. })
  2397. createBatch := func() ts.GermBatch {
  2398. return ts.GermBatch{
  2399. DirtyGerms: map[int32][]*pb.Germ{
  2400. 17: {
  2401. {Stringer: pb.Stringer{X: "germ1"}},
  2402. },
  2403. 18: {
  2404. {Stringer: pb.Stringer{X: "germ2"}},
  2405. {Stringer: pb.Stringer{X: "germ3"}},
  2406. {Stringer: pb.Stringer{X: "germ4"}},
  2407. },
  2408. },
  2409. GermMap: map[int32]*pb.Germ{
  2410. 13: {Stringer: pb.Stringer{X: "germ13"}},
  2411. 21: {Stringer: pb.Stringer{X: "germ21"}},
  2412. },
  2413. DishMap: map[int32]*ts.Dish{
  2414. 0: ts.CreateDish(nil, io.EOF),
  2415. 1: ts.CreateDish(nil, io.ErrUnexpectedEOF),
  2416. 2: ts.CreateDish(&pb.Dish{Stringer: pb.Stringer{X: "dish"}}, nil),
  2417. },
  2418. HasPreviousResult: true,
  2419. DirtyID: 10,
  2420. GermStrain: 421,
  2421. InfectedAt: now,
  2422. }
  2423. }
  2424. return []test{{
  2425. label: label,
  2426. x: createBatch(),
  2427. y: createBatch(),
  2428. wantPanic: "cannot handle unexported field",
  2429. }, {
  2430. label: label,
  2431. x: createBatch(),
  2432. y: createBatch(),
  2433. opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
  2434. }, {
  2435. label: label,
  2436. x: createBatch(),
  2437. y: func() ts.GermBatch {
  2438. gb := createBatch()
  2439. s := gb.DirtyGerms[18]
  2440. s[0], s[1], s[2] = s[1], s[2], s[0]
  2441. return gb
  2442. }(),
  2443. opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish},
  2444. wantDiff: `
  2445. teststructs.GermBatch{
  2446. DirtyGerms: map[int32][]*testprotos.Germ{
  2447. 17: {s"germ1"},
  2448. 18: {
  2449. - s"germ2",
  2450. s"germ3",
  2451. s"germ4",
  2452. + s"germ2",
  2453. },
  2454. },
  2455. CleanGerms: nil,
  2456. GermMap: map[int32]*testprotos.Germ{13: s"germ13", 21: s"germ21"},
  2457. ... // 7 identical fields
  2458. }
  2459. `,
  2460. }, {
  2461. label: label,
  2462. x: createBatch(),
  2463. y: func() ts.GermBatch {
  2464. gb := createBatch()
  2465. s := gb.DirtyGerms[18]
  2466. s[0], s[1], s[2] = s[1], s[2], s[0]
  2467. return gb
  2468. }(),
  2469. opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
  2470. }, {
  2471. label: label,
  2472. x: func() ts.GermBatch {
  2473. gb := createBatch()
  2474. delete(gb.DirtyGerms, 17)
  2475. gb.DishMap[1] = nil
  2476. return gb
  2477. }(),
  2478. y: func() ts.GermBatch {
  2479. gb := createBatch()
  2480. gb.DirtyGerms[18] = gb.DirtyGerms[18][:2]
  2481. gb.GermStrain = 22
  2482. return gb
  2483. }(),
  2484. opts: []cmp.Option{cmp.Comparer(pb.Equal), sortGerms, equalDish},
  2485. wantDiff: `
  2486. teststructs.GermBatch{
  2487. DirtyGerms: map[int32][]*testprotos.Germ{
  2488. + 17: {s"germ1"},
  2489. 18: Inverse(Sort, []*testprotos.Germ{
  2490. s"germ2",
  2491. s"germ3",
  2492. - s"germ4",
  2493. }),
  2494. },
  2495. CleanGerms: nil,
  2496. GermMap: map[int32]*testprotos.Germ{13: s"germ13", 21: s"germ21"},
  2497. DishMap: map[int32]*teststructs.Dish{
  2498. 0: &{err: &errors.errorString{s: "EOF"}},
  2499. - 1: nil,
  2500. + 1: &{err: &errors.errorString{s: "unexpected EOF"}},
  2501. 2: &{pb: &testprotos.Dish{Stringer: testprotos.Stringer{X: "dish"}}},
  2502. },
  2503. HasPreviousResult: true,
  2504. DirtyID: 10,
  2505. CleanID: 0,
  2506. - GermStrain: 421,
  2507. + GermStrain: 22,
  2508. TotalDirtyGerms: 0,
  2509. InfectedAt: s"2009-11-10 23:00:00 +0000 UTC",
  2510. }
  2511. `,
  2512. }}
  2513. }
  2514. func project3Tests() []test {
  2515. const label = "Project3"
  2516. allowVisibility := cmp.AllowUnexported(ts.Dirt{})
  2517. ignoreLocker := cmpopts.IgnoreInterfaces(struct{ sync.Locker }{})
  2518. transformProtos := cmp.Transformer("λ", func(x pb.Dirt) *pb.Dirt {
  2519. return &x
  2520. })
  2521. equalTable := cmp.Comparer(func(x, y ts.Table) bool {
  2522. tx, ok1 := x.(*ts.MockTable)
  2523. ty, ok2 := y.(*ts.MockTable)
  2524. if !ok1 || !ok2 {
  2525. panic("table type must be MockTable")
  2526. }
  2527. return cmp.Equal(tx.State(), ty.State())
  2528. })
  2529. createDirt := func() (d ts.Dirt) {
  2530. d.SetTable(ts.CreateMockTable([]string{"a", "b", "c"}))
  2531. d.SetTimestamp(12345)
  2532. d.Discord = 554
  2533. d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "proto"}}
  2534. d.SetWizard(map[string]*pb.Wizard{
  2535. "harry": {Stringer: pb.Stringer{X: "potter"}},
  2536. "albus": {Stringer: pb.Stringer{X: "dumbledore"}},
  2537. })
  2538. d.SetLastTime(54321)
  2539. return d
  2540. }
  2541. return []test{{
  2542. label: label,
  2543. x: createDirt(),
  2544. y: createDirt(),
  2545. wantPanic: "cannot handle unexported field",
  2546. }, {
  2547. label: label,
  2548. x: createDirt(),
  2549. y: createDirt(),
  2550. opts: []cmp.Option{allowVisibility, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
  2551. wantPanic: "cannot handle unexported field",
  2552. }, {
  2553. label: label,
  2554. x: createDirt(),
  2555. y: createDirt(),
  2556. opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
  2557. }, {
  2558. label: label,
  2559. x: func() ts.Dirt {
  2560. d := createDirt()
  2561. d.SetTable(ts.CreateMockTable([]string{"a", "c"}))
  2562. d.Proto = pb.Dirt{Stringer: pb.Stringer{X: "blah"}}
  2563. return d
  2564. }(),
  2565. y: func() ts.Dirt {
  2566. d := createDirt()
  2567. d.Discord = 500
  2568. d.SetWizard(map[string]*pb.Wizard{
  2569. "harry": {Stringer: pb.Stringer{X: "otter"}},
  2570. })
  2571. return d
  2572. }(),
  2573. opts: []cmp.Option{allowVisibility, transformProtos, ignoreLocker, cmp.Comparer(pb.Equal), equalTable},
  2574. wantDiff: `
  2575. teststructs.Dirt{
  2576. - table: &teststructs.MockTable{state: []string{"a", "c"}},
  2577. + table: &teststructs.MockTable{state: []string{"a", "b", "c"}},
  2578. ts: 12345,
  2579. - Discord: 554,
  2580. + Discord: 500,
  2581. - Proto: testprotos.Dirt(Inverse(λ, s"blah")),
  2582. + Proto: testprotos.Dirt(Inverse(λ, s"proto")),
  2583. wizard: map[string]*testprotos.Wizard{
  2584. - "albus": s"dumbledore",
  2585. - "harry": s"potter",
  2586. + "harry": s"otter",
  2587. },
  2588. sadistic: nil,
  2589. lastTime: 54321,
  2590. ... // 1 ignored field
  2591. }
  2592. `,
  2593. }}
  2594. }
  2595. func project4Tests() []test {
  2596. const label = "Project4"
  2597. allowVisibility := cmp.AllowUnexported(
  2598. ts.Cartel{},
  2599. ts.Headquarter{},
  2600. ts.Poison{},
  2601. )
  2602. transformProtos := cmp.Transformer("λ", func(x pb.Restrictions) *pb.Restrictions {
  2603. return &x
  2604. })
  2605. createCartel := func() ts.Cartel {
  2606. var p ts.Poison
  2607. p.SetPoisonType(5)
  2608. p.SetExpiration(now)
  2609. p.SetManufacturer("acme")
  2610. var hq ts.Headquarter
  2611. hq.SetID(5)
  2612. hq.SetLocation("moon")
  2613. hq.SetSubDivisions([]string{"alpha", "bravo", "charlie"})
  2614. hq.SetMetaData(&pb.MetaData{Stringer: pb.Stringer{X: "metadata"}})
  2615. hq.SetPublicMessage([]byte{1, 2, 3, 4, 5})
  2616. hq.SetHorseBack("abcdef")
  2617. hq.SetStatus(44)
  2618. var c ts.Cartel
  2619. c.Headquarter = hq
  2620. c.SetSource("mars")
  2621. c.SetCreationTime(now)
  2622. c.SetBoss("al capone")
  2623. c.SetPoisons([]*ts.Poison{&p})
  2624. return c
  2625. }
  2626. return []test{{
  2627. label: label,
  2628. x: createCartel(),
  2629. y: createCartel(),
  2630. wantPanic: "cannot handle unexported field",
  2631. }, {
  2632. label: label,
  2633. x: createCartel(),
  2634. y: createCartel(),
  2635. opts: []cmp.Option{allowVisibility, cmp.Comparer(pb.Equal)},
  2636. wantPanic: "cannot handle unexported field",
  2637. }, {
  2638. label: label,
  2639. x: createCartel(),
  2640. y: createCartel(),
  2641. opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
  2642. }, {
  2643. label: label,
  2644. x: func() ts.Cartel {
  2645. d := createCartel()
  2646. var p1, p2 ts.Poison
  2647. p1.SetPoisonType(1)
  2648. p1.SetExpiration(now)
  2649. p1.SetManufacturer("acme")
  2650. p2.SetPoisonType(2)
  2651. p2.SetManufacturer("acme2")
  2652. d.SetPoisons([]*ts.Poison{&p1, &p2})
  2653. return d
  2654. }(),
  2655. y: func() ts.Cartel {
  2656. d := createCartel()
  2657. d.SetSubDivisions([]string{"bravo", "charlie"})
  2658. d.SetPublicMessage([]byte{1, 2, 4, 3, 5})
  2659. return d
  2660. }(),
  2661. opts: []cmp.Option{allowVisibility, transformProtos, cmp.Comparer(pb.Equal)},
  2662. wantDiff: `
  2663. teststructs.Cartel{
  2664. Headquarter: teststructs.Headquarter{
  2665. id: 0x05,
  2666. location: "moon",
  2667. subDivisions: []string{
  2668. - "alpha",
  2669. "bravo",
  2670. "charlie",
  2671. },
  2672. incorporatedDate: s"0001-01-01 00:00:00 +0000 UTC",
  2673. metaData: s"metadata",
  2674. privateMessage: nil,
  2675. publicMessage: []uint8{
  2676. 0x01,
  2677. 0x02,
  2678. - 0x03,
  2679. + 0x04,
  2680. - 0x04,
  2681. + 0x03,
  2682. 0x05,
  2683. },
  2684. horseBack: "abcdef",
  2685. rattle: "",
  2686. ... // 5 identical fields
  2687. },
  2688. source: "mars",
  2689. creationDate: s"0001-01-01 00:00:00 +0000 UTC",
  2690. boss: "al capone",
  2691. lastCrimeDate: s"0001-01-01 00:00:00 +0000 UTC",
  2692. poisons: []*teststructs.Poison{
  2693. &{
  2694. - poisonType: 1,
  2695. + poisonType: 5,
  2696. expiration: s"2009-11-10 23:00:00 +0000 UTC",
  2697. manufacturer: "acme",
  2698. potency: 0,
  2699. },
  2700. - &{poisonType: 2, manufacturer: "acme2"},
  2701. },
  2702. }
  2703. `,
  2704. }}
  2705. }
  2706. // BenchmarkBytes benchmarks the performance of performing Equal or Diff on
  2707. // large slices of bytes.
  2708. func BenchmarkBytes(b *testing.B) {
  2709. // Create a list of PathFilters that never apply, but are evaluated.
  2710. const maxFilters = 5
  2711. var filters cmp.Options
  2712. errorIface := reflect.TypeOf((*error)(nil)).Elem()
  2713. for i := 0; i <= maxFilters; i++ {
  2714. filters = append(filters, cmp.FilterPath(func(p cmp.Path) bool {
  2715. return p.Last().Type().AssignableTo(errorIface) // Never true
  2716. }, cmp.Ignore()))
  2717. }
  2718. type benchSize struct {
  2719. label string
  2720. size int64
  2721. }
  2722. for _, ts := range []benchSize{
  2723. {"4KiB", 1 << 12},
  2724. {"64KiB", 1 << 16},
  2725. {"1MiB", 1 << 20},
  2726. {"16MiB", 1 << 24},
  2727. } {
  2728. bx := append(append(make([]byte, ts.size/2), 'x'), make([]byte, ts.size/2)...)
  2729. by := append(append(make([]byte, ts.size/2), 'y'), make([]byte, ts.size/2)...)
  2730. b.Run(ts.label, func(b *testing.B) {
  2731. // Iteratively add more filters that never apply, but are evaluated
  2732. // to measure the cost of simply evaluating each filter.
  2733. for i := 0; i <= maxFilters; i++ {
  2734. b.Run(fmt.Sprintf("EqualFilter%d", i), func(b *testing.B) {
  2735. b.ReportAllocs()
  2736. b.SetBytes(2 * ts.size)
  2737. for j := 0; j < b.N; j++ {
  2738. cmp.Equal(bx, by, filters[:i]...)
  2739. }
  2740. })
  2741. }
  2742. for i := 0; i <= maxFilters; i++ {
  2743. b.Run(fmt.Sprintf("DiffFilter%d", i), func(b *testing.B) {
  2744. b.ReportAllocs()
  2745. b.SetBytes(2 * ts.size)
  2746. for j := 0; j < b.N; j++ {
  2747. cmp.Diff(bx, by, filters[:i]...)
  2748. }
  2749. })
  2750. }
  2751. })
  2752. }
  2753. }