|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // Copyright 2017, The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE.md file.
-
- package cmp_test
-
- import (
- "fmt"
- "math"
- "net"
- "reflect"
- "sort"
- "strings"
- "time"
-
- "github.com/google/go-cmp/cmp"
- )
-
- // TODO: Re-write these examples in terms of how you actually use the
- // fundamental options and filters and not in terms of what cool things you can
- // do with them since that overlaps with cmp/cmpopts.
-
- // Use Diff to print out a human-readable report of differences for tests
- // comparing nested or structured data.
- func ExampleDiff_testing() {
- // Let got be the hypothetical value obtained from some logic under test
- // and want be the expected golden data.
- got, want := MakeGatewayInfo()
-
- if diff := cmp.Diff(want, got); diff != "" {
- t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff)
- }
-
- // Output:
- // MakeGatewayInfo() mismatch (-want +got):
- // cmp_test.Gateway{
- // SSID: "CoffeeShopWiFi",
- // - IPAddress: s"192.168.0.2",
- // + IPAddress: s"192.168.0.1",
- // NetMask: net.IPMask{0xff, 0xff, 0x00, 0x00},
- // Clients: []cmp_test.Client{
- // ... // 2 identical elements
- // {Hostname: "macchiato", IPAddress: s"192.168.0.153", LastSeen: s"2009-11-10 23:39:43 +0000 UTC"},
- // {Hostname: "espresso", IPAddress: s"192.168.0.121"},
- // {
- // Hostname: "latte",
- // - IPAddress: s"192.168.0.221",
- // + IPAddress: s"192.168.0.219",
- // LastSeen: s"2009-11-10 23:00:23 +0000 UTC",
- // },
- // + {
- // + Hostname: "americano",
- // + IPAddress: s"192.168.0.188",
- // + LastSeen: s"2009-11-10 23:03:05 +0000 UTC",
- // + },
- // },
- // }
- }
-
- // Approximate equality for floats can be handled by defining a custom
- // comparer on floats that determines two values to be equal if they are within
- // some range of each other.
- //
- // This example is for demonstrative purposes; use cmpopts.EquateApprox instead.
- func ExampleOption_approximateFloats() {
- // This Comparer only operates on float64.
- // To handle float32s, either define a similar function for that type
- // or use a Transformer to convert float32s into float64s.
- opt := cmp.Comparer(func(x, y float64) bool {
- delta := math.Abs(x - y)
- mean := math.Abs(x+y) / 2.0
- return delta/mean < 0.00001
- })
-
- x := []float64{1.0, 1.1, 1.2, math.Pi}
- y := []float64{1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi
- z := []float64{1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi
-
- fmt.Println(cmp.Equal(x, y, opt))
- fmt.Println(cmp.Equal(y, z, opt))
- fmt.Println(cmp.Equal(z, x, opt))
-
- // Output:
- // true
- // false
- // false
- }
-
- // Normal floating-point arithmetic defines == to be false when comparing
- // NaN with itself. In certain cases, this is not the desired property.
- //
- // This example is for demonstrative purposes; use cmpopts.EquateNaNs instead.
- func ExampleOption_equalNaNs() {
- // This Comparer only operates on float64.
- // To handle float32s, either define a similar function for that type
- // or use a Transformer to convert float32s into float64s.
- opt := cmp.Comparer(func(x, y float64) bool {
- return (math.IsNaN(x) && math.IsNaN(y)) || x == y
- })
-
- x := []float64{1.0, math.NaN(), math.E, -0.0, +0.0}
- y := []float64{1.0, math.NaN(), math.E, -0.0, +0.0}
- z := []float64{1.0, math.NaN(), math.Pi, -0.0, +0.0} // Pi constant instead of E
-
- fmt.Println(cmp.Equal(x, y, opt))
- fmt.Println(cmp.Equal(y, z, opt))
- fmt.Println(cmp.Equal(z, x, opt))
-
- // Output:
- // true
- // false
- // false
- }
-
- // To have floating-point comparisons combine both properties of NaN being
- // equal to itself and also approximate equality of values, filters are needed
- // to restrict the scope of the comparison so that they are composable.
- //
- // This example is for demonstrative purposes;
- // use cmpopts.EquateNaNs and cmpopts.EquateApprox instead.
- func ExampleOption_equalNaNsAndApproximateFloats() {
- alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
-
- opts := cmp.Options{
- // This option declares that a float64 comparison is equal only if
- // both inputs are NaN.
- cmp.FilterValues(func(x, y float64) bool {
- return math.IsNaN(x) && math.IsNaN(y)
- }, alwaysEqual),
-
- // This option declares approximate equality on float64s only if
- // both inputs are not NaN.
- cmp.FilterValues(func(x, y float64) bool {
- return !math.IsNaN(x) && !math.IsNaN(y)
- }, cmp.Comparer(func(x, y float64) bool {
- delta := math.Abs(x - y)
- mean := math.Abs(x+y) / 2.0
- return delta/mean < 0.00001
- })),
- }
-
- x := []float64{math.NaN(), 1.0, 1.1, 1.2, math.Pi}
- y := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi
- z := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi
-
- fmt.Println(cmp.Equal(x, y, opts))
- fmt.Println(cmp.Equal(y, z, opts))
- fmt.Println(cmp.Equal(z, x, opts))
-
- // Output:
- // true
- // false
- // false
- }
-
- // Sometimes, an empty map or slice is considered equal to an allocated one
- // of zero length.
- //
- // This example is for demonstrative purposes; use cmpopts.EquateEmpty instead.
- func ExampleOption_equalEmpty() {
- alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
-
- // This option handles slices and maps of any type.
- opt := cmp.FilterValues(func(x, y interface{}) bool {
- vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
- return (vx.IsValid() && vy.IsValid() && vx.Type() == vy.Type()) &&
- (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) &&
- (vx.Len() == 0 && vy.Len() == 0)
- }, alwaysEqual)
-
- type S struct {
- A []int
- B map[string]bool
- }
- x := S{nil, make(map[string]bool, 100)}
- y := S{make([]int, 0, 200), nil}
- z := S{[]int{0}, nil} // []int has a single element (i.e., not empty)
-
- fmt.Println(cmp.Equal(x, y, opt))
- fmt.Println(cmp.Equal(y, z, opt))
- fmt.Println(cmp.Equal(z, x, opt))
-
- // Output:
- // true
- // false
- // false
- }
-
- // Two slices may be considered equal if they have the same elements,
- // regardless of the order that they appear in. Transformations can be used
- // to sort the slice.
- //
- // This example is for demonstrative purposes; use cmpopts.SortSlices instead.
- func ExampleOption_sortedSlice() {
- // This Transformer sorts a []int.
- trans := cmp.Transformer("Sort", func(in []int) []int {
- out := append([]int(nil), in...) // Copy input to avoid mutating it
- sort.Ints(out)
- return out
- })
-
- x := struct{ Ints []int }{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
- y := struct{ Ints []int }{[]int{2, 8, 0, 9, 6, 1, 4, 7, 3, 5}}
- z := struct{ Ints []int }{[]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 8}}
-
- fmt.Println(cmp.Equal(x, y, trans))
- fmt.Println(cmp.Equal(y, z, trans))
- fmt.Println(cmp.Equal(z, x, trans))
-
- // Output:
- // true
- // false
- // false
- }
-
- type otherString string
-
- func (x otherString) Equal(y otherString) bool {
- return strings.ToLower(string(x)) == strings.ToLower(string(y))
- }
-
- // If the Equal method defined on a type is not suitable, the type can be be
- // dynamically transformed to be stripped of the Equal method (or any method
- // for that matter).
- func ExampleOption_avoidEqualMethod() {
- // Suppose otherString.Equal performs a case-insensitive equality,
- // which is too loose for our needs.
- // We can avoid the methods of otherString by declaring a new type.
- type myString otherString
-
- // This transformer converts otherString to myString, allowing Equal to use
- // other Options to determine equality.
- trans := cmp.Transformer("", func(in otherString) myString {
- return myString(in)
- })
-
- x := []otherString{"foo", "bar", "baz"}
- y := []otherString{"fOO", "bAr", "Baz"} // Same as before, but with different case
-
- fmt.Println(cmp.Equal(x, y)) // Equal because of case-insensitivity
- fmt.Println(cmp.Equal(x, y, trans)) // Not equal because of more exact equality
-
- // Output:
- // true
- // false
- }
-
- func roundF64(z float64) float64 {
- if z < 0 {
- return math.Ceil(z - 0.5)
- }
- return math.Floor(z + 0.5)
- }
-
- // The complex numbers complex64 and complex128 can really just be decomposed
- // into a pair of float32 or float64 values. It would be convenient to be able
- // define only a single comparator on float64 and have float32, complex64, and
- // complex128 all be able to use that comparator. Transformations can be used
- // to handle this.
- func ExampleOption_transformComplex() {
- opts := []cmp.Option{
- // This transformer decomposes complex128 into a pair of float64s.
- cmp.Transformer("T1", func(in complex128) (out struct{ Real, Imag float64 }) {
- out.Real, out.Imag = real(in), imag(in)
- return out
- }),
- // This transformer converts complex64 to complex128 to allow the
- // above transform to take effect.
- cmp.Transformer("T2", func(in complex64) complex128 {
- return complex128(in)
- }),
- // This transformer converts float32 to float64.
- cmp.Transformer("T3", func(in float32) float64 {
- return float64(in)
- }),
- // This equality function compares float64s as rounded integers.
- cmp.Comparer(func(x, y float64) bool {
- return roundF64(x) == roundF64(y)
- }),
- }
-
- x := []interface{}{
- complex128(3.0), complex64(5.1 + 2.9i), float32(-1.2), float64(12.3),
- }
- y := []interface{}{
- complex128(3.1), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7),
- }
- z := []interface{}{
- complex128(3.8), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7),
- }
-
- fmt.Println(cmp.Equal(x, y, opts...))
- fmt.Println(cmp.Equal(y, z, opts...))
- fmt.Println(cmp.Equal(z, x, opts...))
-
- // Output:
- // true
- // false
- // false
- }
-
- type (
- Gateway struct {
- SSID string
- IPAddress net.IP
- NetMask net.IPMask
- Clients []Client
- }
- Client struct {
- Hostname string
- IPAddress net.IP
- LastSeen time.Time
- }
- )
-
- func MakeGatewayInfo() (x, y Gateway) {
- x = Gateway{
- SSID: "CoffeeShopWiFi",
- IPAddress: net.IPv4(192, 168, 0, 1),
- NetMask: net.IPv4Mask(255, 255, 0, 0),
- Clients: []Client{{
- Hostname: "ristretto",
- IPAddress: net.IPv4(192, 168, 0, 116),
- }, {
- Hostname: "aribica",
- IPAddress: net.IPv4(192, 168, 0, 104),
- LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC),
- }, {
- Hostname: "macchiato",
- IPAddress: net.IPv4(192, 168, 0, 153),
- LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC),
- }, {
- Hostname: "espresso",
- IPAddress: net.IPv4(192, 168, 0, 121),
- }, {
- Hostname: "latte",
- IPAddress: net.IPv4(192, 168, 0, 219),
- LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC),
- }, {
- Hostname: "americano",
- IPAddress: net.IPv4(192, 168, 0, 188),
- LastSeen: time.Date(2009, time.November, 10, 23, 3, 5, 0, time.UTC),
- }},
- }
- y = Gateway{
- SSID: "CoffeeShopWiFi",
- IPAddress: net.IPv4(192, 168, 0, 2),
- NetMask: net.IPv4Mask(255, 255, 0, 0),
- Clients: []Client{{
- Hostname: "ristretto",
- IPAddress: net.IPv4(192, 168, 0, 116),
- }, {
- Hostname: "aribica",
- IPAddress: net.IPv4(192, 168, 0, 104),
- LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC),
- }, {
- Hostname: "macchiato",
- IPAddress: net.IPv4(192, 168, 0, 153),
- LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC),
- }, {
- Hostname: "espresso",
- IPAddress: net.IPv4(192, 168, 0, 121),
- }, {
- Hostname: "latte",
- IPAddress: net.IPv4(192, 168, 0, 221),
- LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC),
- }},
- }
- return x, y
- }
-
- var t fakeT
-
- type fakeT struct{}
-
- func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) }
|