...

Source file src/github.com/google/go-cmp/cmp/example_test.go

Documentation: github.com/google/go-cmp/cmp

     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  
     5  package cmp_test
     6  
     7  import (
     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.
    25  func 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.
    66  func 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.
    95  func 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.
   123  func 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.
   163  func 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.
   198  func 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  
   220  type otherString string
   221  
   222  func (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).
   229  func 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  
   252  func 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.
   264  func 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  
   306  type (
   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  
   320  func 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  
   376  var t fakeT
   377  
   378  type fakeT struct{}
   379  
   380  func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) }
   381  

View as plain text