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