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
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:   {0xff, 0xff, 0x00, 0x00},
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, +0.0}
102	y := []float64{1.0, math.NaN(), math.E, -0.0, +0.0}
103	z := []float64{1.0, math.NaN(), math.Pi, -0.0, +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.ToLower(string(x)) == strings.ToLower(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