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