You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

377 lines
11 KiB

  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. package cmp_test
  5. import (
  6. "fmt"
  7. "math"
  8. "net"
  9. "reflect"
  10. "sort"
  11. "strings"
  12. "time"
  13. "github.com/google/go-cmp/cmp"
  14. )
  15. // TODO: Re-write these examples in terms of how you actually use the
  16. // fundamental options and filters and not in terms of what cool things you can
  17. // do with them since that overlaps with cmp/cmpopts.
  18. // Use Diff to print out a human-readable report of differences for tests
  19. // comparing nested or structured data.
  20. func ExampleDiff_testing() {
  21. // Let got be the hypothetical value obtained from some logic under test
  22. // and want be the expected golden data.
  23. got, want := MakeGatewayInfo()
  24. if diff := cmp.Diff(want, got); diff != "" {
  25. t.Errorf("MakeGatewayInfo() mismatch (-want +got):\n%s", diff)
  26. }
  27. // Output:
  28. // MakeGatewayInfo() mismatch (-want +got):
  29. // cmp_test.Gateway{
  30. // SSID: "CoffeeShopWiFi",
  31. // - IPAddress: s"192.168.0.2",
  32. // + IPAddress: s"192.168.0.1",
  33. // NetMask: net.IPMask{0xff, 0xff, 0x00, 0x00},
  34. // Clients: []cmp_test.Client{
  35. // ... // 2 identical elements
  36. // {Hostname: "macchiato", IPAddress: s"192.168.0.153", LastSeen: s"2009-11-10 23:39:43 +0000 UTC"},
  37. // {Hostname: "espresso", IPAddress: s"192.168.0.121"},
  38. // {
  39. // Hostname: "latte",
  40. // - IPAddress: s"192.168.0.221",
  41. // + IPAddress: s"192.168.0.219",
  42. // LastSeen: s"2009-11-10 23:00:23 +0000 UTC",
  43. // },
  44. // + {
  45. // + Hostname: "americano",
  46. // + IPAddress: s"192.168.0.188",
  47. // + LastSeen: s"2009-11-10 23:03:05 +0000 UTC",
  48. // + },
  49. // },
  50. // }
  51. }
  52. // Approximate equality for floats can be handled by defining a custom
  53. // comparer on floats that determines two values to be equal if they are within
  54. // some range of each other.
  55. //
  56. // This example is for demonstrative purposes; use cmpopts.EquateApprox instead.
  57. func ExampleOption_approximateFloats() {
  58. // This Comparer only operates on float64.
  59. // To handle float32s, either define a similar function for that type
  60. // or use a Transformer to convert float32s into float64s.
  61. opt := cmp.Comparer(func(x, y float64) bool {
  62. delta := math.Abs(x - y)
  63. mean := math.Abs(x+y) / 2.0
  64. return delta/mean < 0.00001
  65. })
  66. x := []float64{1.0, 1.1, 1.2, math.Pi}
  67. y := []float64{1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi
  68. z := []float64{1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi
  69. fmt.Println(cmp.Equal(x, y, opt))
  70. fmt.Println(cmp.Equal(y, z, opt))
  71. fmt.Println(cmp.Equal(z, x, opt))
  72. // Output:
  73. // true
  74. // false
  75. // false
  76. }
  77. // Normal floating-point arithmetic defines == to be false when comparing
  78. // NaN with itself. In certain cases, this is not the desired property.
  79. //
  80. // This example is for demonstrative purposes; use cmpopts.EquateNaNs instead.
  81. func ExampleOption_equalNaNs() {
  82. // This Comparer only operates on float64.
  83. // To handle float32s, either define a similar function for that type
  84. // or use a Transformer to convert float32s into float64s.
  85. opt := cmp.Comparer(func(x, y float64) bool {
  86. return (math.IsNaN(x) && math.IsNaN(y)) || x == y
  87. })
  88. x := []float64{1.0, math.NaN(), math.E, -0.0, +0.0}
  89. y := []float64{1.0, math.NaN(), math.E, -0.0, +0.0}
  90. z := []float64{1.0, math.NaN(), math.Pi, -0.0, +0.0} // Pi constant instead of E
  91. fmt.Println(cmp.Equal(x, y, opt))
  92. fmt.Println(cmp.Equal(y, z, opt))
  93. fmt.Println(cmp.Equal(z, x, opt))
  94. // Output:
  95. // true
  96. // false
  97. // false
  98. }
  99. // To have floating-point comparisons combine both properties of NaN being
  100. // equal to itself and also approximate equality of values, filters are needed
  101. // to restrict the scope of the comparison so that they are composable.
  102. //
  103. // This example is for demonstrative purposes;
  104. // use cmpopts.EquateNaNs and cmpopts.EquateApprox instead.
  105. func ExampleOption_equalNaNsAndApproximateFloats() {
  106. alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
  107. opts := cmp.Options{
  108. // This option declares that a float64 comparison is equal only if
  109. // both inputs are NaN.
  110. cmp.FilterValues(func(x, y float64) bool {
  111. return math.IsNaN(x) && math.IsNaN(y)
  112. }, alwaysEqual),
  113. // This option declares approximate equality on float64s only if
  114. // both inputs are not NaN.
  115. cmp.FilterValues(func(x, y float64) bool {
  116. return !math.IsNaN(x) && !math.IsNaN(y)
  117. }, cmp.Comparer(func(x, y float64) bool {
  118. delta := math.Abs(x - y)
  119. mean := math.Abs(x+y) / 2.0
  120. return delta/mean < 0.00001
  121. })),
  122. }
  123. x := []float64{math.NaN(), 1.0, 1.1, 1.2, math.Pi}
  124. y := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.14159265359} // Accurate enough to Pi
  125. z := []float64{math.NaN(), 1.0, 1.1, 1.2, 3.1415} // Diverges too far from Pi
  126. fmt.Println(cmp.Equal(x, y, opts))
  127. fmt.Println(cmp.Equal(y, z, opts))
  128. fmt.Println(cmp.Equal(z, x, opts))
  129. // Output:
  130. // true
  131. // false
  132. // false
  133. }
  134. // Sometimes, an empty map or slice is considered equal to an allocated one
  135. // of zero length.
  136. //
  137. // This example is for demonstrative purposes; use cmpopts.EquateEmpty instead.
  138. func ExampleOption_equalEmpty() {
  139. alwaysEqual := cmp.Comparer(func(_, _ interface{}) bool { return true })
  140. // This option handles slices and maps of any type.
  141. opt := cmp.FilterValues(func(x, y interface{}) bool {
  142. vx, vy := reflect.ValueOf(x), reflect.ValueOf(y)
  143. return (vx.IsValid() && vy.IsValid() && vx.Type() == vy.Type()) &&
  144. (vx.Kind() == reflect.Slice || vx.Kind() == reflect.Map) &&
  145. (vx.Len() == 0 && vy.Len() == 0)
  146. }, alwaysEqual)
  147. type S struct {
  148. A []int
  149. B map[string]bool
  150. }
  151. x := S{nil, make(map[string]bool, 100)}
  152. y := S{make([]int, 0, 200), nil}
  153. z := S{[]int{0}, nil} // []int has a single element (i.e., not empty)
  154. fmt.Println(cmp.Equal(x, y, opt))
  155. fmt.Println(cmp.Equal(y, z, opt))
  156. fmt.Println(cmp.Equal(z, x, opt))
  157. // Output:
  158. // true
  159. // false
  160. // false
  161. }
  162. // Two slices may be considered equal if they have the same elements,
  163. // regardless of the order that they appear in. Transformations can be used
  164. // to sort the slice.
  165. //
  166. // This example is for demonstrative purposes; use cmpopts.SortSlices instead.
  167. func ExampleOption_sortedSlice() {
  168. // This Transformer sorts a []int.
  169. trans := cmp.Transformer("Sort", func(in []int) []int {
  170. out := append([]int(nil), in...) // Copy input to avoid mutating it
  171. sort.Ints(out)
  172. return out
  173. })
  174. x := struct{ Ints []int }{[]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}
  175. y := struct{ Ints []int }{[]int{2, 8, 0, 9, 6, 1, 4, 7, 3, 5}}
  176. z := struct{ Ints []int }{[]int{0, 0, 1, 2, 3, 4, 5, 6, 7, 8}}
  177. fmt.Println(cmp.Equal(x, y, trans))
  178. fmt.Println(cmp.Equal(y, z, trans))
  179. fmt.Println(cmp.Equal(z, x, trans))
  180. // Output:
  181. // true
  182. // false
  183. // false
  184. }
  185. type otherString string
  186. func (x otherString) Equal(y otherString) bool {
  187. return strings.ToLower(string(x)) == strings.ToLower(string(y))
  188. }
  189. // If the Equal method defined on a type is not suitable, the type can be be
  190. // dynamically transformed to be stripped of the Equal method (or any method
  191. // for that matter).
  192. func ExampleOption_avoidEqualMethod() {
  193. // Suppose otherString.Equal performs a case-insensitive equality,
  194. // which is too loose for our needs.
  195. // We can avoid the methods of otherString by declaring a new type.
  196. type myString otherString
  197. // This transformer converts otherString to myString, allowing Equal to use
  198. // other Options to determine equality.
  199. trans := cmp.Transformer("", func(in otherString) myString {
  200. return myString(in)
  201. })
  202. x := []otherString{"foo", "bar", "baz"}
  203. y := []otherString{"fOO", "bAr", "Baz"} // Same as before, but with different case
  204. fmt.Println(cmp.Equal(x, y)) // Equal because of case-insensitivity
  205. fmt.Println(cmp.Equal(x, y, trans)) // Not equal because of more exact equality
  206. // Output:
  207. // true
  208. // false
  209. }
  210. func roundF64(z float64) float64 {
  211. if z < 0 {
  212. return math.Ceil(z - 0.5)
  213. }
  214. return math.Floor(z + 0.5)
  215. }
  216. // The complex numbers complex64 and complex128 can really just be decomposed
  217. // into a pair of float32 or float64 values. It would be convenient to be able
  218. // define only a single comparator on float64 and have float32, complex64, and
  219. // complex128 all be able to use that comparator. Transformations can be used
  220. // to handle this.
  221. func ExampleOption_transformComplex() {
  222. opts := []cmp.Option{
  223. // This transformer decomposes complex128 into a pair of float64s.
  224. cmp.Transformer("T1", func(in complex128) (out struct{ Real, Imag float64 }) {
  225. out.Real, out.Imag = real(in), imag(in)
  226. return out
  227. }),
  228. // This transformer converts complex64 to complex128 to allow the
  229. // above transform to take effect.
  230. cmp.Transformer("T2", func(in complex64) complex128 {
  231. return complex128(in)
  232. }),
  233. // This transformer converts float32 to float64.
  234. cmp.Transformer("T3", func(in float32) float64 {
  235. return float64(in)
  236. }),
  237. // This equality function compares float64s as rounded integers.
  238. cmp.Comparer(func(x, y float64) bool {
  239. return roundF64(x) == roundF64(y)
  240. }),
  241. }
  242. x := []interface{}{
  243. complex128(3.0), complex64(5.1 + 2.9i), float32(-1.2), float64(12.3),
  244. }
  245. y := []interface{}{
  246. complex128(3.1), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7),
  247. }
  248. z := []interface{}{
  249. complex128(3.8), complex64(4.9 + 3.1i), float32(-1.3), float64(11.7),
  250. }
  251. fmt.Println(cmp.Equal(x, y, opts...))
  252. fmt.Println(cmp.Equal(y, z, opts...))
  253. fmt.Println(cmp.Equal(z, x, opts...))
  254. // Output:
  255. // true
  256. // false
  257. // false
  258. }
  259. type (
  260. Gateway struct {
  261. SSID string
  262. IPAddress net.IP
  263. NetMask net.IPMask
  264. Clients []Client
  265. }
  266. Client struct {
  267. Hostname string
  268. IPAddress net.IP
  269. LastSeen time.Time
  270. }
  271. )
  272. func MakeGatewayInfo() (x, y Gateway) {
  273. x = Gateway{
  274. SSID: "CoffeeShopWiFi",
  275. IPAddress: net.IPv4(192, 168, 0, 1),
  276. NetMask: net.IPv4Mask(255, 255, 0, 0),
  277. Clients: []Client{{
  278. Hostname: "ristretto",
  279. IPAddress: net.IPv4(192, 168, 0, 116),
  280. }, {
  281. Hostname: "aribica",
  282. IPAddress: net.IPv4(192, 168, 0, 104),
  283. LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC),
  284. }, {
  285. Hostname: "macchiato",
  286. IPAddress: net.IPv4(192, 168, 0, 153),
  287. LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC),
  288. }, {
  289. Hostname: "espresso",
  290. IPAddress: net.IPv4(192, 168, 0, 121),
  291. }, {
  292. Hostname: "latte",
  293. IPAddress: net.IPv4(192, 168, 0, 219),
  294. LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC),
  295. }, {
  296. Hostname: "americano",
  297. IPAddress: net.IPv4(192, 168, 0, 188),
  298. LastSeen: time.Date(2009, time.November, 10, 23, 3, 5, 0, time.UTC),
  299. }},
  300. }
  301. y = Gateway{
  302. SSID: "CoffeeShopWiFi",
  303. IPAddress: net.IPv4(192, 168, 0, 2),
  304. NetMask: net.IPv4Mask(255, 255, 0, 0),
  305. Clients: []Client{{
  306. Hostname: "ristretto",
  307. IPAddress: net.IPv4(192, 168, 0, 116),
  308. }, {
  309. Hostname: "aribica",
  310. IPAddress: net.IPv4(192, 168, 0, 104),
  311. LastSeen: time.Date(2009, time.November, 10, 23, 6, 32, 0, time.UTC),
  312. }, {
  313. Hostname: "macchiato",
  314. IPAddress: net.IPv4(192, 168, 0, 153),
  315. LastSeen: time.Date(2009, time.November, 10, 23, 39, 43, 0, time.UTC),
  316. }, {
  317. Hostname: "espresso",
  318. IPAddress: net.IPv4(192, 168, 0, 121),
  319. }, {
  320. Hostname: "latte",
  321. IPAddress: net.IPv4(192, 168, 0, 221),
  322. LastSeen: time.Date(2009, time.November, 10, 23, 0, 23, 0, time.UTC),
  323. }},
  324. }
  325. return x, y
  326. }
  327. var t fakeT
  328. type fakeT struct{}
  329. func (t fakeT) Errorf(format string, args ...interface{}) { fmt.Printf(format+"\n", args...) }