aboutsummaryrefslogtreecommitdiff
path: root/cmp/cmpopts
diff options
context:
space:
mode:
authorJoe Tsai <joetsai@digital-static.net>2018-03-26 15:28:54 -0700
committerGitHub <noreply@github.com>2018-03-26 15:28:54 -0700
commit7d086766fb84312590ea843913dc5b8949a485c5 (patch)
tree042c548e7c08dc9d8aed2bdbb2ac779c256dbeb8 /cmp/cmpopts
parent0627e4440b70a1645f19cd9f611320f547068b45 (diff)
downloadgo-cmp-7d086766fb84312590ea843913dc5b8949a485c5.tar.gz
Add AcyclicTransformer helper (#82)
Add AcyclicTransformer helper, which wraps Transformer with a FilterPath that ensures that the transformer cannot be recursively applied upon the output of itself. Adjust documentation of Transformer to suggest usage of AcyclicTransformer if necessary. Fixes #77
Diffstat (limited to 'cmp/cmpopts')
-rw-r--r--cmp/cmpopts/util_test.go33
-rw-r--r--cmp/cmpopts/xform.go35
2 files changed, 68 insertions, 0 deletions
diff --git a/cmp/cmpopts/util_test.go b/cmp/cmpopts/util_test.go
index 02c447f..790ba8d 100644
--- a/cmp/cmpopts/util_test.go
+++ b/cmp/cmpopts/util_test.go
@@ -716,6 +716,33 @@ func TestOptions(t *testing.T) {
},
wantEqual: true,
reason: "equal because all Ignore options can be composed together",
+ }, {
+ label: "AcyclicTransformer",
+ x: "a\nb\nc\nd",
+ y: "a\nb\nd\nd",
+ opts: []cmp.Option{
+ AcyclicTransformer("", func(s string) []string { return strings.Split(s, "\n") }),
+ },
+ wantEqual: false,
+ reason: "not equal because 3rd line differs, but should not recurse infinitely",
+ }, {
+ label: "AcyclicTransformer",
+ x: []string{"foo", "Bar", "BAZ"},
+ y: []string{"Foo", "BAR", "baz"},
+ opts: []cmp.Option{
+ AcyclicTransformer("", func(s string) string { return strings.ToUpper(s) }),
+ },
+ wantEqual: true,
+ reason: "equal because of strings.ToUpper; AcyclicTransformer unnecessary, but check this still works",
+ }, {
+ label: "AcyclicTransformer",
+ x: "this is a sentence",
+ y: "this is a sentence",
+ opts: []cmp.Option{
+ AcyclicTransformer("", func(s string) []string { return strings.Fields(s) }),
+ },
+ wantEqual: true,
+ reason: "equal because acyclic transformer splits on any contiguous whitespace",
}}
for _, tt := range tests {
@@ -938,6 +965,12 @@ func TestPanic(t *testing.T) {
fnc: IgnoreUnexported,
args: args(Foo1{}, struct{ x, X int }{}),
reason: "input may be named or unnamed structs",
+ }, {
+ label: "AcyclicTransformer",
+ fnc: AcyclicTransformer,
+ args: args("", "not a func"),
+ wantPanic: "invalid transformer function",
+ reason: "AcyclicTransformer has same input requirements as Transformer",
}}
for _, tt := range tests {
diff --git a/cmp/cmpopts/xform.go b/cmp/cmpopts/xform.go
new file mode 100644
index 0000000..be278f1
--- /dev/null
+++ b/cmp/cmpopts/xform.go
@@ -0,0 +1,35 @@
+// Copyright 2018, 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 cmpopts
+
+import (
+ "github.com/google/go-cmp/cmp"
+)
+
+type xformFilter struct{ xform cmp.Option }
+
+func (xf xformFilter) filter(p cmp.Path) bool {
+ for _, ps := range p {
+ if t, ok := ps.(cmp.Transform); ok && t.Option() == xf.xform {
+ return false
+ }
+ }
+ return true
+}
+
+// AcyclicTransformer returns a Transformer with a filter applied that ensures
+// that the transformer cannot be recursively applied upon its own output.
+//
+// An example use case is a transformer that splits a string by lines:
+// AcyclicTransformer("SplitLines", func(s string) []string{
+// return strings.Split(s, "\n")
+// })
+//
+// Had this been an unfiltered Transformer instead, this would result in an
+// infinite cycle converting a string to []string to [][]string and so on.
+func AcyclicTransformer(name string, f interface{}) cmp.Option {
+ xf := xformFilter{cmp.Transformer(name, f)}
+ return cmp.FilterPath(xf.filter, xf.xform)
+}