github.com/integration-system/go-cmp@v0.0.0-20190131081942-ac5582987a2f/cmp/example_test.go (about) 1 // Copyright 2017, The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE.md file. 4 5 package cmp_test 6 7 import ( 8 "fmt" 9 "math" 10 "reflect" 11 "sort" 12 "strings" 13 14 "github.com/integration-system/go-cmp/cmp" 15 ) 16 17 // TODO: Re-write these examples in terms of how you actually use the 18 // fundamental options and filters and not in terms of what cool things you can 19 // do with them since that overlaps with cmp/cmpopts. 20 21 // Use Diff for printing out human-readable errors for test cases comparing 22 // nested or structured data. 23 func ExampleDiff_testing() { 24 // Code under test: 25 type ShipManifest struct { 26 Name string 27 Crew map[string]string 28 Androids int 29 Stolen bool 30 } 31 32 // AddCrew tries to add the given crewmember to the manifest. 33 AddCrew := func(m *ShipManifest, name, title string) { 34 if m.Crew == nil { 35 m.Crew = make(map[string]string) 36 } 37 m.Crew[title] = name 38 } 39 40 // Test function: 41 tests := []struct { 42 desc string 43 before *ShipManifest 44 name, title string 45 after *ShipManifest 46 }{ 47 { 48 desc: "add to empty", 49 before: &ShipManifest{}, 50 name: "Zaphod Beeblebrox", 51 title: "Galactic President", 52 after: &ShipManifest{ 53 Crew: map[string]string{ 54 "Zaphod Beeblebrox": "Galactic President", 55 }, 56 }, 57 }, 58 { 59 desc: "add another", 60 before: &ShipManifest{ 61 Crew: map[string]string{ 62 "Zaphod Beeblebrox": "Galactic President", 63 }, 64 }, 65 name: "Trillian", 66 title: "Human", 67 after: &ShipManifest{ 68 Crew: map[string]string{ 69 "Zaphod Beeblebrox": "Galactic President", 70 "Trillian": "Human", 71 }, 72 }, 73 }, 74 { 75 desc: "overwrite", 76 before: &ShipManifest{ 77 Crew: map[string]string{ 78 "Zaphod Beeblebrox": "Galactic President", 79 }, 80 }, 81 name: "Zaphod Beeblebrox", 82 title: "Just this guy, you know?", 83 after: &ShipManifest{ 84 Crew: map[string]string{ 85 "Zaphod Beeblebrox": "Just this guy, you know?", 86 }, 87 }, 88 }, 89 } 90 91 var t fakeT 92 for _, test := range tests { 93 AddCrew(test.before, test.name, test.title) 94 if diff := cmp.Diff(test.before, test.after); diff != "" { 95 t.Errorf("%s: after AddCrew, manifest differs: (-want +got)\n%s", test.desc, diff) 96 } 97 } 98 99 // Output: 100 // add to empty: after AddCrew, manifest differs: (-want +got) 101 // {*cmp_test.ShipManifest}.Crew["Galactic President"]: 102 // -: "Zaphod Beeblebrox" 103 // +: <non-existent> 104 // {*cmp_test.ShipManifest}.Crew["Zaphod Beeblebrox"]: 105 // -: <non-existent> 106 // +: "Galactic President" 107 // 108 // add another: after AddCrew, manifest differs: (-want +got) 109 // {*cmp_test.ShipManifest}.Crew["Human"]: 110 // -: "Trillian" 111 // +: <non-existent> 112 // {*cmp_test.ShipManifest}.Crew["Trillian"]: 113 // -: <non-existent> 114 // +: "Human" 115 // 116 // overwrite: after AddCrew, manifest differs: (-want +got) 117 // {*cmp_test.ShipManifest}.Crew["Just this guy, you know?"]: 118 // -: "Zaphod Beeblebrox" 119 // +: <non-existent> 120 // {*cmp_test.ShipManifest}.Crew["Zaphod Beeblebrox"]: 121 // -: "Galactic President" 122 // +: "Just this guy, you know?" 123 } 124 125 // Approximate equality for floats can be handled by defining a custom 126 // comparer on floats that determines two values to be equal if they are within 127 // some range of each other. 128 // 129 // This example is for demonstrative purposes; use cmpopts.EquateApprox instead. 130 func ExampleOption_approximateFloats() { 131 // This Comparer only operates on float64. 132 // To handle float32s, either define a similar function for that type 133 // or use a Transformer to convert float32s into float64s. 134 opt := cmp.Comparer(func(x, y float64) bool { 135 delta := math.Abs(x - y) 136 mean := math.Abs(x+y) / 2.0 137 return delta/mean < 0.00001 138 }) 139 140 x := []float64{1.0, 1.1, 1.2, math.Pi} 141 y := []float64{1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi 142 z := []float64{1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi 143 144 fmt.Println(cmp.Equal(x, y, opt)) 145 fmt.Println(cmp.Equal(y, z, opt)) 146 fmt.Println(cmp.Equal(z, x, opt)) 147 148 // Output: 149 // true 150 // false 151 // false 152 } 153 154 // Normal floating-point arithmetic defines == to be false when comparing 155 // NaN with itself. In certain cases, this is not the desired property. 156 // 157 // This example is for demonstrative purposes; use cmpopts.EquateNaNs instead. 158 func ExampleOption_equalNaNs() { 159 // This Comparer only operates on float64. 160 // To handle float32s, either define a similar function for that type 161 // or use a Transformer to convert float32s into float64s. 162 opt := cmp.Comparer(func(x, y float64) bool { 163 return (math.IsNaN(x) && math.IsNaN(y)) || x == y 164 }) 165 166 x := []float64{1.0, math.NaN(), math.E, -0.0, +0.0} 167 y := []float64{1.0, math.NaN(), math.E, -0.0, +0.0} 168 z := []float64{1.0, math.NaN(), math.Pi, -0.0, +0.0} // Pi constant instead of E 169 170 fmt.Println(cmp.Equal(x, y, opt)) 171 fmt.Println(cmp.Equal(y, z, opt)) 172 fmt.Println(cmp.Equal(z, x, opt)) 173 174 // Output: 175 // true 176 // false 177 // false 178 } 179 180 // To have floating-point comparisons combine both properties of NaN being 181 // equal to itself and also approximate equality of values, filters are needed 182 // to restrict the scope of the comparison so that they are composable. 183 // 184 // This example is for demonstrative purposes; 185 // use cmpopts.EquateNaNs and cmpopts.EquateApprox instead. 186 func ExampleOption_equalNaNsAndApproximateFloats() { 187 alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true }) 188 189 opts := cmp.Options{ 190 // This option declares that a float64 comparison is equal only if 191 // both inputs are NaN. 192 cmp.FilterValues(func(x, y float64) bool { 193 return math.IsNaN(x) && math.IsNaN(y) 194 }, alwaysEqual), 195 196 // This option declares approximate equality on float64s only if 197 // both inputs are not NaN. 198 cmp.FilterValues(func(x, y float64) bool { 199 return !math.IsNaN(x) && !math.IsNaN(y) 200 }, cmp.Comparer(func(x, y float64) bool { 201 delta := math.Abs(x - y) 202 mean := math.Abs(x+y) / 2.0 203 return delta/mean < 0.00001 204 })), 205 } 206 207 x := []float64{math.NaN(), 1.0, 1.1, 1.2, math.Pi} 208 y := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi 209 z := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi 210 211 fmt.Println(cmp.Equal(x, y, opts)) 212 fmt.Println(cmp.Equal(y, z, opts)) 213 fmt.Println(cmp.Equal(z, x, opts)) 214 215 // Output: 216 // true 217 // false 218 // false 219 } 220 221 // Sometimes, an empty map or slice is considered equal to an allocated one 222 // of zero length. 223 // 224 // This example is for demonstrative purposes; use cmpopts.EquateEmpty instead. 225 func ExampleOption_equalEmpty() { 226 alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true }) 227 228 // This option handles slices and maps of any type. 229 opt := cmp.FilterValues(func(x, y interface{}) bool { 230 vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) 231 return (vx.IsValid() && vy.IsValid() && vx.Type() == vy.Type()) && 232 (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) && 233 (vx.Len() == 0 && vy.Len() == 0) 234 }, alwaysEqual) 235 236 type S struct { 237 A []int 238 B map[string]bool 239 } 240 x := S{nil, make(map[string]bool, 100)} 241 y := S{make([]int, 0, 200), nil} 242 z := S{[]int{0}, nil} // []int has a single element (i.e., not empty) 243 244 fmt.Println(cmp.Equal(x, y, opt)) 245 fmt.Println(cmp.Equal(y, z, opt)) 246 fmt.Println(cmp.Equal(z, x, opt)) 247 248 // Output: 249 // true 250 // false 251 // false 252 } 253 254 // Two slices may be considered equal if they have the same elements, 255 // regardless of the order that they appear in. Transformations can be used 256 // to sort the slice. 257 // 258 // This example is for demonstrative purposes; use cmpopts.SortSlices instead. 259 func ExampleOption_sortedSlice() { 260 // This Transformer sorts a []int. 261 trans := cmp.Transformer("Sort", func(in []int) []int { 262 out := append([]int(nil), in...) // Copy input to avoid mutating it 263 sort.Ints(out) 264 return out 265 }) 266 267 x := struct{ Ints []int }{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}} 268 y := struct{ Ints []int }{[]int{2, 8, 0, 9, 6, 1, 4, 7, 3, 5}} 269 z := struct{ Ints []int }{[]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 8}} 270 271 fmt.Println(cmp.Equal(x, y, trans)) 272 fmt.Println(cmp.Equal(y, z, trans)) 273 fmt.Println(cmp.Equal(z, x, trans)) 274 275 // Output: 276 // true 277 // false 278 // false 279 } 280 281 type otherString string 282 283 func (x otherString) Equal(y otherString) bool { 284 return strings.ToLower(string(x)) == strings.ToLower(string(y)) 285 } 286 287 // If the Equal method defined on a type is not suitable, the type can be be 288 // dynamically transformed to be stripped of the Equal method (or any method 289 // for that matter). 290 func ExampleOption_avoidEqualMethod() { 291 // Suppose otherString.Equal performs a case-insensitive equality, 292 // which is too loose for our needs. 293 // We can avoid the methods of otherString by declaring a new type. 294 type myString otherString 295 296 // This transformer converts otherString to myString, allowing Equal to use 297 // other Options to determine equality. 298 trans := cmp.Transformer("", func(in otherString) myString { 299 return myString(in) 300 }) 301 302 x := []otherString{"foo", "bar", "baz"} 303 y := []otherString{"fOO", "bAr", "Baz"} // Same as before, but with different case 304 305 fmt.Println(cmp.Equal(x, y)) // Equal because of case-insensitivity 306 fmt.Println(cmp.Equal(x, y, trans)) // Not equal because of more exact equality 307 308 // Output: 309 // true 310 // false 311 } 312 313 func roundF64(z float64) float64 { 314 if z < 0 { 315 return math.Ceil(z - 0.5) 316 } 317 return math.Floor(z + 0.5) 318 } 319 320 // The complex numbers complex64 and complex128 can really just be decomposed 321 // into a pair of float32 or float64 values. It would be convenient to be able 322 // define only a single comparator on float64 and have float32, complex64, and 323 // complex128 all be able to use that comparator. Transformations can be used 324 // to handle this. 325 func ExampleOption_transformComplex() { 326 opts := []cmp.Option{ 327 // This transformer decomposes complex128 into a pair of float64s. 328 cmp.Transformer("T1", func(in complex128) (out struct{ Real, Imag float64 }) { 329 out.Real, out.Imag = real(in), imag(in) 330 return out 331 }), 332 // This transformer converts complex64 to complex128 to allow the 333 // above transform to take effect. 334 cmp.Transformer("T2", func(in complex64) complex128 { 335 return complex128(in) 336 }), 337 // This transformer converts float32 to float64. 338 cmp.Transformer("T3", func(in float32) float64 { 339 return float64(in) 340 }), 341 // This equality function compares float64s as rounded integers. 342 cmp.Comparer(func(x, y float64) bool { 343 return roundF64(x) == roundF64(y) 344 }), 345 } 346 347 x := []interface{}{ 348 complex128(3.0), complex64(5.1 + 2.9i), float32(-1.2), float64(12.3), 349 } 350 y := []interface{}{ 351 complex128(3.1), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7), 352 } 353 z := []interface{}{ 354 complex128(3.8), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7), 355 } 356 357 fmt.Println(cmp.Equal(x, y, opts...)) 358 fmt.Println(cmp.Equal(y, z, opts...)) 359 fmt.Println(cmp.Equal(z, x, opts...)) 360 361 // Output: 362 // true 363 // false 364 // false 365 } 366 367 type fakeT struct{} 368 369 func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) }