• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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