Commit da15c40d authored by Francesco Banconi's avatar Francesco Banconi

Fix handling of verbosity in Cmp based checkers.

Checkers created at compiler time now correctly handle
verbosity.
Also update go-cmp to take advantage of new diff output,
and drop support for go v1.7 as new go-cmp uses sort.Slice
that was introduced by go v1.8.
parent 16fea45c
...@@ -2,14 +2,15 @@ language: go ...@@ -2,14 +2,15 @@ language: go
go_import_path: github.com/frankban/quicktest go_import_path: github.com/frankban/quicktest
go: go:
- "1.7"
- "1.8" - "1.8"
- "1.9" - "1.9"
- "1.10" - "1.10"
- "1.11"
- 1.x - 1.x
- master - master
script: script:
- go get -v github.com/rogpeppe/godeps - go get -v github.com/rogpeppe/godeps
- $GOPATH/bin/godeps -u dependencies.tsv - $GOPATH/bin/godeps -u dependencies.tsv
- GO111MODULE=on go test ./...
- GO111MODULE=on go test -v ./... - GO111MODULE=on go test -v ./...
# Make is only required when using Go < 1.11.
# On newer Go versions dependency management is handled by Go modules.
default: test
$(GOPATH)/bin/godeps:
go get -v github.com/rogpeppe/godeps
deps: $(GOPATH)/bin/godeps
$(GOPATH)/bin/godeps -u dependencies.tsv
create-deps: $(GOPATH)/bin/godeps
$(GOPATH)/bin/godeps -t ./... > dependencies.tsv
.PHONY: install
install: deps
go install -v ./...
.PHONY: test
test: deps
go test -v ./...
...@@ -79,10 +79,10 @@ func (c *equalsChecker) Check(got interface{}, args []interface{}, note func(key ...@@ -79,10 +79,10 @@ func (c *equalsChecker) Check(got interface{}, args []interface{}, note func(key
// c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals. // c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals.
// //
func CmpEquals(opts ...cmp.Option) Checker { func CmpEquals(opts ...cmp.Option) Checker {
return cmpEquals(testing.Verbose(), opts...) return cmpEquals(testing.Verbose, opts...)
} }
func cmpEquals(verbose bool, opts ...cmp.Option) Checker { func cmpEquals(verbose func() bool, opts ...cmp.Option) Checker {
return &cmpEqualsChecker{ return &cmpEqualsChecker{
argNames: []string{"got", "want"}, argNames: []string{"got", "want"},
opts: opts, opts: opts,
...@@ -93,7 +93,7 @@ func cmpEquals(verbose bool, opts ...cmp.Option) Checker { ...@@ -93,7 +93,7 @@ func cmpEquals(verbose bool, opts ...cmp.Option) Checker {
type cmpEqualsChecker struct { type cmpEqualsChecker struct {
argNames argNames
opts cmp.Options opts cmp.Options
verbose bool verbose func() bool
} }
// Check implements Checker.Check by checking that got == args[0] according to // Check implements Checker.Check by checking that got == args[0] according to
...@@ -110,7 +110,7 @@ func (c *cmpEqualsChecker) Check(got interface{}, args []interface{}, note func( ...@@ -110,7 +110,7 @@ func (c *cmpEqualsChecker) Check(got interface{}, args []interface{}, note func(
want := args[0] want := args[0]
if diff := cmp.Diff(got, want, c.opts...); diff != "" { if diff := cmp.Diff(got, want, c.opts...); diff != "" {
// Only output values when the verbose flag is set. // Only output values when the verbose flag is set.
if c.verbose { if c.verbose() {
note("diff (-got +want)", Unquoted(diff)) note("diff (-got +want)", Unquoted(diff))
return errors.New("values are not deep equal") return errors.New("values are not deep equal")
} }
......
...@@ -23,9 +23,6 @@ var ( ...@@ -23,9 +23,6 @@ var (
ch <- 47 ch <- 47
return ch return ch
}() }()
)
var (
sameInts = cmpopts.SortSlices(func(x, y int) bool { sameInts = cmpopts.SortSlices(func(x, y int) bool {
return x < y return x < y
}) })
...@@ -50,6 +47,7 @@ var checkerTests = []struct { ...@@ -50,6 +47,7 @@ var checkerTests = []struct {
checker qt.Checker checker qt.Checker
got interface{} got interface{}
args []interface{} args []interface{}
verbose bool
expectedCheckFailure string expectedCheckFailure string
expectedNegateFailure string expectedNegateFailure string
}{{ }{{
...@@ -240,7 +238,7 @@ want args: ...@@ -240,7 +238,7 @@ want args:
`, `,
}, { }, {
about: "CmpEquals: same values", about: "CmpEquals: same values",
checker: qt.InternalCmpEquals(false), checker: qt.CmpEquals(),
got: cmpEqualsGot, got: cmpEqualsGot,
args: []interface{}{cmpEqualsGot}, args: []interface{}{cmpEqualsGot},
expectedNegateFailure: ` expectedNegateFailure: `
...@@ -259,7 +257,7 @@ want: ...@@ -259,7 +257,7 @@ want:
`, `,
}, { }, {
about: "CmpEquals: different values", about: "CmpEquals: different values",
checker: qt.InternalCmpEquals(false), checker: qt.CmpEquals(),
got: cmpEqualsGot, got: cmpEqualsGot,
args: []interface{}{cmpEqualsWant}, args: []interface{}{cmpEqualsWant},
expectedCheckFailure: fmt.Sprintf(` expectedCheckFailure: fmt.Sprintf(`
...@@ -270,9 +268,10 @@ diff (-got +want): ...@@ -270,9 +268,10 @@ diff (-got +want):
`, diff(cmpEqualsGot, cmpEqualsWant)), `, diff(cmpEqualsGot, cmpEqualsWant)),
}, { }, {
about: "CmpEquals: different values: verbose", about: "CmpEquals: different values: verbose",
checker: qt.InternalCmpEquals(true), checker: qt.CmpEquals(),
got: cmpEqualsGot, got: cmpEqualsGot,
args: []interface{}{cmpEqualsWant}, args: []interface{}{cmpEqualsWant},
verbose: true,
expectedCheckFailure: fmt.Sprintf(` expectedCheckFailure: fmt.Sprintf(`
error: error:
values are not deep equal values are not deep equal
...@@ -297,7 +296,7 @@ want: ...@@ -297,7 +296,7 @@ want:
`, diff(cmpEqualsGot, cmpEqualsWant)), `, diff(cmpEqualsGot, cmpEqualsWant)),
}, { }, {
about: "CmpEquals: same values with options", about: "CmpEquals: same values with options",
checker: qt.InternalCmpEquals(false, sameInts), checker: qt.CmpEquals(sameInts),
got: []int{1, 2, 3}, got: []int{1, 2, 3},
args: []interface{}{ args: []interface{}{
[]int{3, 2, 1}, []int{3, 2, 1},
...@@ -312,7 +311,7 @@ want: ...@@ -312,7 +311,7 @@ want:
`, `,
}, { }, {
about: "CmpEquals: different values with options", about: "CmpEquals: different values with options",
checker: qt.InternalCmpEquals(false, sameInts), checker: qt.CmpEquals(sameInts),
got: []int{1, 2, 4}, got: []int{1, 2, 4},
args: []interface{}{ args: []interface{}{
[]int{3, 2, 1}, []int{3, 2, 1},
...@@ -325,11 +324,12 @@ diff (-got +want): ...@@ -325,11 +324,12 @@ diff (-got +want):
`, diff([]int{1, 2, 4}, []int{3, 2, 1}, sameInts)), `, diff([]int{1, 2, 4}, []int{3, 2, 1}, sameInts)),
}, { }, {
about: "CmpEquals: different values with options: verbose", about: "CmpEquals: different values with options: verbose",
checker: qt.InternalCmpEquals(true, sameInts), checker: qt.CmpEquals(sameInts),
got: []int{1, 2, 4}, got: []int{1, 2, 4},
args: []interface{}{ args: []interface{}{
[]int{3, 2, 1}, []int{3, 2, 1},
}, },
verbose: true,
expectedCheckFailure: fmt.Sprintf(` expectedCheckFailure: fmt.Sprintf(`
error: error:
values are not deep equal values are not deep equal
...@@ -342,7 +342,7 @@ want: ...@@ -342,7 +342,7 @@ want:
`, diff([]int{1, 2, 4}, []int{3, 2, 1}, sameInts)), `, diff([]int{1, 2, 4}, []int{3, 2, 1}, sameInts)),
}, { }, {
about: "CmpEquals: structs with unexported fields not allowed", about: "CmpEquals: structs with unexported fields not allowed",
checker: qt.InternalCmpEquals(false), checker: qt.CmpEquals(),
got: struct{ answer int }{ got: struct{ answer int }{
answer: 42, answer: 42,
}, },
...@@ -354,7 +354,7 @@ want: ...@@ -354,7 +354,7 @@ want:
expectedCheckFailure: ` expectedCheckFailure: `
error: error:
cannot handle unexported field: root.answer cannot handle unexported field: root.answer
consider using AllowUnexported or cmpopts.IgnoreUnexported consider using a custom Comparer; if you control the implementation of type, you can also consider AllowUnexported or cmpopts.IgnoreUnexported
got: got:
struct { answer int }{answer:42} struct { answer int }{answer:42}
want: want:
...@@ -362,7 +362,7 @@ want: ...@@ -362,7 +362,7 @@ want:
`, `,
}, { }, {
about: "CmpEquals: structs with unexported fields ignored", about: "CmpEquals: structs with unexported fields ignored",
checker: qt.InternalCmpEquals(false, cmpopts.IgnoreUnexported(struct{ answer int }{})), checker: qt.CmpEquals(cmpopts.IgnoreUnexported(struct{ answer int }{})),
got: struct{ answer int }{ got: struct{ answer int }{
answer: 42, answer: 42,
}, },
...@@ -381,7 +381,7 @@ want: ...@@ -381,7 +381,7 @@ want:
`, `,
}, { }, {
about: "CmpEquals: same times", about: "CmpEquals: same times",
checker: qt.InternalCmpEquals(false), checker: qt.CmpEquals(),
got: goTime, got: goTime,
args: []interface{}{ args: []interface{}{
goTime, goTime,
...@@ -396,11 +396,12 @@ want: ...@@ -396,11 +396,12 @@ want:
`, `,
}, { }, {
about: "CmpEquals: different times: verbose", about: "CmpEquals: different times: verbose",
checker: qt.InternalCmpEquals(true), checker: qt.CmpEquals(),
got: goTime.Add(24 * time.Hour), got: goTime.Add(24 * time.Hour),
args: []interface{}{ args: []interface{}{
goTime, goTime,
}, },
verbose: true,
expectedCheckFailure: fmt.Sprintf(` expectedCheckFailure: fmt.Sprintf(`
error: error:
values are not deep equal values are not deep equal
...@@ -413,7 +414,7 @@ want: ...@@ -413,7 +414,7 @@ want:
`, diff(goTime.Add(24*time.Hour), goTime)), `, diff(goTime.Add(24*time.Hour), goTime)),
}, { }, {
about: "CmpEquals: not enough arguments", about: "CmpEquals: not enough arguments",
checker: qt.InternalCmpEquals(false), checker: qt.CmpEquals(),
expectedCheckFailure: ` expectedCheckFailure: `
error: error:
bad check: not enough arguments provided to checker: got 0, want 1 bad check: not enough arguments provided to checker: got 0, want 1
...@@ -428,7 +429,7 @@ want args: ...@@ -428,7 +429,7 @@ want args:
`, `,
}, { }, {
about: "CmpEquals: too many arguments", about: "CmpEquals: too many arguments",
checker: qt.InternalCmpEquals(false), checker: qt.CmpEquals(),
got: []int{42}, got: []int{42},
args: []interface{}{[]int{42}, "bad wolf"}, args: []interface{}{[]int{42}, "bad wolf"},
expectedCheckFailure: ` expectedCheckFailure: `
...@@ -453,6 +454,45 @@ got args: ...@@ -453,6 +454,45 @@ got args:
want args: want args:
want want
`, `,
}, {
about: "DeepEquals: different values",
checker: qt.DeepEquals,
got: cmpEqualsGot,
args: []interface{}{cmpEqualsWant},
expectedCheckFailure: fmt.Sprintf(`
error:
values are not deep equal
diff (-got +want):
%s
`, diff(cmpEqualsGot, cmpEqualsWant)),
}, {
about: "DeepEquals: different values: verbose",
checker: qt.DeepEquals,
got: cmpEqualsGot,
args: []interface{}{cmpEqualsWant},
verbose: true,
expectedCheckFailure: fmt.Sprintf(`
error:
values are not deep equal
diff (-got +want):
%s
got:
struct { Strings []interface {}; Ints []int }{
Strings: {
"who",
"dalek",
},
Ints: {42, 47},
}
want:
struct { Strings []interface {}; Ints []int }{
Strings: {
"who",
"dalek",
},
Ints: {42},
}
`, diff(cmpEqualsGot, cmpEqualsWant)),
}, { }, {
about: "ContentEquals: same values", about: "ContentEquals: same values",
checker: qt.ContentEquals, checker: qt.ContentEquals,
...@@ -1852,16 +1892,17 @@ want args: ...@@ -1852,16 +1892,17 @@ want args:
func TestCheckers(t *testing.T) { func TestCheckers(t *testing.T) {
for _, test := range checkerTests { for _, test := range checkerTests {
checker := qt.WithVerbosity(test.checker, test.verbose)
t.Run(test.about, func(t *testing.T) { t.Run(test.about, func(t *testing.T) {
tt := &testingT{} tt := &testingT{}
c := qt.New(tt) c := qt.New(tt)
ok := c.Check(test.got, test.checker, test.args...) ok := c.Check(test.got, checker, test.args...)
checkResult(t, ok, tt.errorString(), test.expectedCheckFailure) checkResult(t, ok, tt.errorString(), test.expectedCheckFailure)
}) })
t.Run("Not "+test.about, func(t *testing.T) { t.Run("Not "+test.about, func(t *testing.T) {
tt := &testingT{} tt := &testingT{}
c := qt.New(tt) c := qt.New(tt)
ok := c.Check(test.got, qt.Not(test.checker), test.args...) ok := c.Check(test.got, qt.Not(checker), test.args...)
checkResult(t, ok, tt.errorString(), test.expectedNegateFailure) checkResult(t, ok, tt.errorString(), test.expectedNegateFailure)
}) })
} }
......
github.com/google/go-cmp git 3af367b6b30c263d47e8895973edcca9a49cf029 2018-02-02T21:19:42Z github.com/google/go-cmp git 6f77996f0c42f7b84e5a2b252227263f93432e9b 2019-03-12T03:24:27Z
github.com/kr/pretty git 73f6ac0b30a98e433b289500d779f50c1a6f0712 2018-05-06T08:33:45Z github.com/kr/pretty git 73f6ac0b30a98e433b289500d779f50c1a6f0712 2018-05-06T08:33:45Z
github.com/kr/text git e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f 2018-05-06T08:24:08Z github.com/kr/text git e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f 2018-05-06T08:24:08Z
...@@ -2,7 +2,17 @@ ...@@ -2,7 +2,17 @@
package quicktest package quicktest
var ( var Prefixf = prefixf
InternalCmpEquals = cmpEquals
Prefixf = prefixf // WithVerbosity returns the given checker with a verbosity level of v.
) // A copy of the original checker is made if mutating it is required.
func WithVerbosity(c Checker, v bool) Checker {
if c, ok := c.(*cmpEqualsChecker); ok {
c := *c
c.verbose = func() bool {
return v
}
return &c
}
return c
}
module github.com/frankban/quicktest module github.com/frankban/quicktest
require ( require (
github.com/google/go-cmp v0.2.0 github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42
github.com/kr/pretty v0.1.0 github.com/kr/pretty v0.1.0
) )
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42 h1:q3pnF5JFBNRz8sRD+IRj7Y6DMyYGTNqnZ9axTbSfoNI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
......
...@@ -89,8 +89,70 @@ stack: ...@@ -89,8 +89,70 @@ stack:
assertReport(t, tt, want) assertReport(t, tt, want)
} }
func TestCmpReportOutput(t *testing.T) {
tt := &testingT{}
c := qt.New(tt)
gotExamples := []*reportExample{{
AnInt: 42,
ASlice: []string{},
}, {
AnInt: 47,
ASlice: []string{"these", "are", "the", "voyages"},
}, {
AnInt: 1,
}, {
AnInt: 2,
}, {
ASlice: []string{"foo", "bar"},
}}
wantExamples := []*reportExample{{
AnInt: 42,
}, {
AnInt: 47,
ASlice: []string{"these", "are", "the", "voyages"},
}, {
AnInt: 2,
}, {
AnInt: 1,
}, {
ASlice: []string{"foo"},
}, {}}
checker := qt.WithVerbosity(qt.DeepEquals, false)
c.Assert(gotExamples, checker, wantExamples)
want := `
error:
values are not deep equal
diff (-got +want):
[]*quicktest_test.reportExample{
&{
AnInt: 42,
- ASlice: []string{},
+ ASlice: nil,
},
&{AnInt: 47, ASlice: []string{"these", "are", "the", "voyages"}},
+ &{AnInt: 2},
&{AnInt: 1},
- &{AnInt: 2},
&{
AnInt: 0,
ASlice: []string{
"foo",
- "bar",
},
},
+ &{},
}
stack:
$file:121
c.Assert(gotExamples, checker, wantExamples)
`
assertReport(t, tt, want)
}
func assertReport(t *testing.T, tt *testingT, want string) { func assertReport(t *testing.T, tt *testingT, want string) {
got := strings.Replace(tt.fatalString(), "\t", " ", -1) got := strings.Replace(tt.fatalString(), "\t", " ", -1)
// go-cmp can include non-breaking spaces in its output.
got = strings.Replace(got, "\u00a0", " ", -1)
// Adjust for file names in different systems. // Adjust for file names in different systems.
_, file, _, ok := runtime.Caller(0) _, file, _, ok := runtime.Caller(0)
assertBool(t, ok, true) assertBool(t, ok, true)
...@@ -105,9 +167,16 @@ func assertReport(t *testing.T, tt *testingT, want string) { ...@@ -105,9 +167,16 @@ func assertReport(t *testing.T, tt *testingT, want string) {
want = strings.Replace(want, "$line", strconv.Itoa(line), 1) want = strings.Replace(want, "$line", strconv.Itoa(line), 1)
if got != want { if got != want {
t.Fatalf(`failure: t.Fatalf(`failure:
%q
%q
------------------------------ got ------------------------------ ------------------------------ got ------------------------------
%s------------------------------ want ----------------------------- %s------------------------------ want -----------------------------
%s-----------------------------------------------------------------`, %s-----------------------------------------------------------------`,
got, want) got, want, got, want)
} }
} }
type reportExample struct {
AnInt int
ASlice []string
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment