pax_global_header00006660000000000000000000000064141332546540014521gustar00rootroot0000000000000052 comment=6de35003a44b0594642075564efc188ca4c47c15 quicktest-1.14.0/000077500000000000000000000000001413325465400136205ustar00rootroot00000000000000quicktest-1.14.0/.github/000077500000000000000000000000001413325465400151605ustar00rootroot00000000000000quicktest-1.14.0/.github/dependabot.yaml000066400000000000000000000005011413325465400201450ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: # Check for updates to GitHub Actions every weekday. interval: "daily" - package-ecosystem: "gomod" directory: "/" schedule: # Check for updates to go modules every weekday. interval: "daily" quicktest-1.14.0/.github/workflows/000077500000000000000000000000001413325465400172155ustar00rootroot00000000000000quicktest-1.14.0/.github/workflows/ci.yaml000066400000000000000000000012271413325465400204760ustar00rootroot00000000000000name: CI on: [push, pull_request] jobs: build_test: name: Build and Test strategy: matrix: go: ['1.11', '1.12', '1.13', '1.14', '1.15', '1.16', '1.17'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2.3.5 - uses: actions/setup-go@v2.1.4 with: go-version: ${{ matrix.go }} stable: false - uses: actions/cache@v2.1.6 with: path: ~/go/pkg/mod key: ubuntu-go-${{ hashFiles('**/go.sum') }} restore-keys: | ubuntu-go- - name: Test run: go test -mod readonly -race ./... - name: Test Verbose run: go test -mod readonly -race -v ./... quicktest-1.14.0/.gitignore000066400000000000000000000000101413325465400155770ustar00rootroot00000000000000.vscode quicktest-1.14.0/.godocdown.template000066400000000000000000000010421413325465400174130ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/frankban/quicktest?status.svg)](https://godoc.org/github.com/frankban/quicktest) [![Build Status](https://github.com/frankban/quicktest/actions/workflows/ci.yaml/badge.svg)](https://github.com/frankban/quicktest/actions/workflows/ci.yaml) [//]: # (Generated with: godocdown -template=.godocdown.template -o README.md) # quicktest `go get github.com/frankban/quicktest` {{ .EmitSynopsis }} For a complete API reference, see the [package documentation](https://pkg.go.dev/github.com/frankban/quicktest). quicktest-1.14.0/LICENSE000066400000000000000000000020571413325465400146310ustar00rootroot00000000000000MIT License Copyright (c) 2017 Canonical Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. quicktest-1.14.0/README.md000066400000000000000000000236041413325465400151040ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/frankban/quicktest?status.svg)](https://godoc.org/github.com/frankban/quicktest) [![Build Status](https://github.com/frankban/quicktest/actions/workflows/ci.yaml/badge.svg)](https://github.com/frankban/quicktest/actions/workflows/ci.yaml) [//]: # (Generated with: godocdown -template=.godocdown.template -o README.md) # quicktest `go get github.com/frankban/quicktest` Package quicktest provides a collection of Go helpers for writing tests. Quicktest helpers can be easily integrated inside regular Go tests, for instance: import qt "github.com/frankban/quicktest" func TestFoo(t *testing.T) { t.Run("numbers", func(t *testing.T) { c := qt.New(t) numbers, err := somepackage.Numbers() c.Assert(numbers, qt.DeepEquals, []int{42, 47}) c.Assert(err, qt.ErrorMatches, "bad wolf") }) t.Run("nil", func(t *testing.T) { c := qt.New(t) got := somepackage.MaybeNil() c.Assert(got, qt.IsNil, qt.Commentf("value: %v", somepackage.Value)) }) } ### Assertions An assertion looks like this, where qt.Equals could be replaced by any available checker. If the assertion fails, the underlying Fatal method is called to describe the error and abort the test. c := qt.New(t) c.Assert(someValue, qt.Equals, wantValue) If you don’t want to abort on failure, use Check instead, which calls Error instead of Fatal: c.Check(someValue, qt.Equals, wantValue) For really short tests, the extra line for instantiating *qt.C can be avoided: qt.Assert(t, someValue, qt.Equals, wantValue) qt.Check(t, someValue, qt.Equals, wantValue) The library provides some base checkers like Equals, DeepEquals, Matches, ErrorMatches, IsNil and others. More can be added by implementing the Checker interface. Below, we list the checkers implemented by the package in alphabetical order. ### All All returns a Checker that uses the given checker to check elements of slice or array or the values of a map. It succeeds if all elements pass the check. On failure it prints the error from the first index that failed. For example: c.Assert([]int{3, 5, 8}, qt.All(qt.Not(qt.Equals)), 0) c.Assert([][]string{{"a", "b"}, {"a", "b"}}, qt.All(qt.DeepEquals), []string{"c", "d"}) See also Any and Contains. ### Any Any returns a Checker that uses the given checker to check elements of a slice or array or the values from a map. It succeeds if any element passes the check. For example: c.Assert([]int{3,5,7,99}, qt.Any(qt.Equals), 7) c.Assert([][]string{{"a", "b"}, {"c", "d"}}, qt.Any(qt.DeepEquals), []string{"c", "d"}) See also All and Contains. ### CmpEquals CmpEquals checks equality of two arbitrary values according to the provided compare options. DeepEquals is more commonly used when no compare options are required. Example calls: c.Assert(list, qt.CmpEquals(cmpopts.SortSlices), []int{42, 47}) c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals. ### CodecEquals CodecEquals returns a checker that checks for codec value equivalence. func CodecEquals( marshal func(interface{}) ([]byte, error), unmarshal func([]byte, interface{}) error, opts ...cmp.Option, ) Checker It expects two arguments: a byte slice or a string containing some codec-marshaled data, and a Go value. It uses unmarshal to unmarshal the data into an interface{} value. It marshals the Go value using marshal, then unmarshals the result into an interface{} value. It then checks that the two interface{} values are deep-equal to one another, using CmpEquals(opts) to perform the check. See JSONEquals for an example of this in use. ### Contains Contains checks that a map, slice, array or string contains a value. It's the same as using Any(Equals), except that it has a special case for strings - if the first argument is a string, the second argument must also be a string and strings.Contains will be used. For example: c.Assert("hello world", qt.Contains, "world") c.Assert([]int{3,5,7,99}, qt.Contains, 7) ### ContentEquals ContentEquals is is like DeepEquals but any slices in the compared values will be sorted before being compared. For example: c.Assert([]string{"c", "a", "b"}, qt.ContentEquals, []string{"a", "b", "c"}) ### DeepEquals DeepEquals checks that two arbitrary values are deeply equal. The comparison is done using the github.com/google/go-cmp/cmp package. When comparing structs, by default no exported fields are allowed. If a more sophisticated comparison is required, use CmpEquals (see below). Example call: c.Assert(got, qt.DeepEquals, []int{42, 47}) ### Equals Equals checks that two values are equal, as compared with Go's == operator. For instance: c.Assert(answer, qt.Equals, 42) Note that the following will fail: c.Assert((*sometype)(nil), qt.Equals, nil) Use the IsNil checker below for this kind of nil check. ### ErrorAs ErrorAs checks that the error is or wraps a specific error type. If so, it assigns it to the provided pointer. This is analogous to calling errors.As. For instance: // Checking for a specific error type c.Assert(err, qt.ErrorAs, new(*os.PathError)) // Checking fields on a specific error type var pathError *os.PathError if c.Check(err, qt.ErrorAs, &pathError) { c.Assert(pathError.Path, Equals, "some_path") } ### ErrorIs ErrorIs checks that the error is or wraps a specific error value. This is analogous to calling errors.Is. For instance: c.Assert(err, qt.ErrorIs, os.ErrNotExist) ### ErrorMatches ErrorMatches checks that the provided value is an error whose message matches the provided regular expression. For instance: c.Assert(err, qt.ErrorMatches, `bad wolf .*`) ### HasLen HasLen checks that the provided value has the given length. For instance: c.Assert([]int{42, 47}, qt.HasLen, 2) c.Assert(myMap, qt.HasLen, 42) ### Implements Implements checks that the provided value implements an interface. The interface is specified with a pointer to an interface variable. For instance: var rc io.ReadCloser c.Assert(myReader, qt.Implements, &rc) ### IsFalse IsFalse checks that the provided value is false. The value must have a boolean underlying type. For instance: c.Assert(false, qt.IsFalse) c.Assert(IsValid(), qt.IsFalse) ### IsNil IsNil checks that the provided value is nil. For instance: c.Assert(got, qt.IsNil) As a special case, if the value is nil but implements the error interface, it is still considered to be non-nil. This means that IsNil will fail on an error value that happens to have an underlying nil value, because that's invariably a mistake. See https://golang.org/doc/faq#nil_error. So it's just fine to check an error like this: c.Assert(err, qt.IsNil) ### IsNotNil IsNotNil is a Checker checking that the provided value is not nil. IsNotNil is the equivalent of qt.Not(qt.IsNil) For instance: c.Assert(got, qt.IsNotNil) ### IsTrue IsTrue checks that the provided value is true. The value must have a boolean underlying type. For instance: c.Assert(true, qt.IsTrue) c.Assert(myBoolean(false), qt.IsTrue) ### JSONEquals JSONEquals checks whether a byte slice or string is JSON-equivalent to a Go value. See CodecEquals for more information. It uses DeepEquals to do the comparison. If a more sophisticated comparison is required, use CodecEquals directly. For instance: c.Assert(`{"First": 47.11}`, qt.JSONEquals, &MyStruct{First: 47.11}) ### Matches Matches checks that a string or result of calling the String method (if the value implements fmt.Stringer) matches the provided regular expression. For instance: c.Assert("these are the voyages", qt.Matches, `these are .*`) c.Assert(net.ParseIP("1.2.3.4"), qt.Matches, `1.*`) ### Not Not returns a Checker negating the given Checker. For instance: c.Assert(got, qt.Not(qt.IsNil)) c.Assert(answer, qt.Not(qt.Equals), 42) ### PanicMatches PanicMatches checks that the provided function panics with a message matching the provided regular expression. For instance: c.Assert(func() {panic("bad wolf ...")}, qt.PanicMatches, `bad wolf .*`) ### Satisfies Satisfies checks that the provided value, when used as argument of the provided predicate function, causes the function to return true. The function must be of type func(T) bool, having got assignable to T. For instance: // Check that an error from os.Open satisfies os.IsNotExist. c.Assert(err, qt.Satisfies, os.IsNotExist) // Check that a floating point number is a not-a-number. c.Assert(f, qt.Satisfies, math.IsNaN) ### Deferred Execution The testing.TB.Cleanup helper provides the ability to defer the execution of functions that will be run when the test completes. This is often useful for creating OS-level resources such as temporary directories (see c.Mkdir). When targeting Go versions that don't have Cleanup (< 1.14), the same can be achieved using c.Defer. In this case, to trigger the deferred behavior, calling c.Done is required. For instance, if you create a *C instance at the top level, you’ll have to add a defer to trigger the cleanups at the end of the test: defer c.Done() However, if you use quicktest to create a subtest, Done will be called automatically at the end of that subtest. For example: func TestFoo(t *testing.T) { c := qt.New(t) c.Run("subtest", func(c *qt.C) { c.Setenv("HOME", c.Mkdir()) // Here $HOME is set the path to a newly created directory. // At the end of the test the directory will be removed // and HOME set back to its original value. }) } The c.Patch, c.Setenv, c.Unsetenv and c.Mkdir helpers use t.Cleanup for cleaning up resources when available, and fall back to Defer otherwise. For a complete API reference, see the [package documentation](https://pkg.go.dev/github.com/frankban/quicktest). quicktest-1.14.0/checker.go000066400000000000000000000572551413325465400155710ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "encoding/json" "errors" "fmt" "reflect" "regexp" "strings" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/kr/pretty" ) // Checker is implemented by types used as part of Check/Assert invocations. type Checker interface { // Check checks that the obtained value (got) is correct with respect to // the checker's arguments (args). On failure, the returned error is // printed along with the checker arguments and any key-value pairs added // by calling the note function. Values are pretty-printed unless they are // of type Unquoted. // // When the check arguments are invalid, Check may return a BadCheck error, // which suppresses printing of the checker arguments. Values added with // note are still printed. // // If Check returns ErrSilent, neither the checker arguments nor the error // are printed. Again, values added with note are still printed. Check(got interface{}, args []interface{}, note func(key string, value interface{})) error // ArgNames returns the names of all required arguments, including the // mandatory got argument and any additional args. ArgNames() []string } // Equals is a Checker checking equality of two comparable values. // // For instance: // // c.Assert(answer, qt.Equals, 42) // // Note that the following will fail: // // c.Assert((*sometype)(nil), qt.Equals, nil) // // Use the IsNil checker below for this kind of nil check. var Equals Checker = &equalsChecker{ argNames: []string{"got", "want"}, } type equalsChecker struct { argNames } // Check implements Checker.Check by checking that got == args[0]. func (c *equalsChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { defer func() { // A panic is raised when the provided values are not comparable. if r := recover(); r != nil { err = fmt.Errorf("%s", r) } }() want := args[0] if got == want { return nil } // Customize error message for non-nil errors. if _, ok := got.(error); ok && want == nil { return errors.New("got non-nil error") } // Show error types when comparing errors with different types. if got, ok := got.(error); ok { if want, ok := want.(error); ok { gotType := reflect.TypeOf(got) wantType := reflect.TypeOf(want) if gotType != wantType { note("got type", Unquoted(gotType.String())) note("want type", Unquoted(wantType.String())) } } return errors.New("values are not equal") } // Show line diff when comparing different multi-line strings. if got, ok := got.(string); ok { if want, ok := want.(string); ok { isMultiLine := func(s string) bool { i := strings.Index(s, "\n") return i != -1 && i < len(s)-1 } if isMultiLine(got) || isMultiLine(want) { diff := cmp.Diff(strings.SplitAfter(got, "\n"), strings.SplitAfter(want, "\n")) note("line diff (-got +want)", Unquoted(diff)) } } } return errors.New("values are not equal") } // CmpEquals returns a Checker checking equality of two arbitrary values // according to the provided compare options. See DeepEquals as an example of // such a checker, commonly used when no compare options are required. // // Example calls: // // c.Assert(list, qt.CmpEquals(cmpopts.SortSlices), []int{42, 47}) // c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals. // func CmpEquals(opts ...cmp.Option) Checker { return cmpEquals(testing.Verbose, opts...) } func cmpEquals(verbose func() bool, opts ...cmp.Option) Checker { return &cmpEqualsChecker{ argNames: []string{"got", "want"}, opts: opts, verbose: verbose, } } type cmpEqualsChecker struct { argNames opts cmp.Options verbose func() bool } // Check implements Checker.Check by checking that got == args[0] according to // the compare options stored in the checker. func (c *cmpEqualsChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { defer func() { // A panic is raised in some cases, for instance when trying to compare // structs with unexported fields and neither AllowUnexported nor // cmpopts.IgnoreUnexported are provided. if r := recover(); r != nil { err = fmt.Errorf("%s", r) } }() want := args[0] if diff := cmp.Diff(got, want, c.opts...); diff != "" { // Only output values when the verbose flag is set. if c.verbose() { note("diff (-got +want)", Unquoted(diff)) return errors.New("values are not deep equal") } note("error", Unquoted("values are not deep equal")) note("diff (-got +want)", Unquoted(diff)) return ErrSilent } return nil } // DeepEquals is a Checker deeply checking equality of two arbitrary values. // The comparison is done using the github.com/google/go-cmp/cmp package. // When comparing structs, by default no exported fields are allowed. CmpEquals // can be used when more customized compare options are required. // // Example call: // // c.Assert(got, qt.DeepEquals, []int{42, 47}) // var DeepEquals = CmpEquals() // ContentEquals is like DeepEquals but any slices in the compared values will // be sorted before being compared. var ContentEquals = CmpEquals(cmpopts.SortSlices(func(x, y interface{}) bool { // TODO frankban: implement a proper sort function. return pretty.Sprint(x) < pretty.Sprint(y) })) // Matches is a Checker checking that the provided string or fmt.Stringer // matches the provided regular expression pattern. // // For instance: // // c.Assert("these are the voyages", qt.Matches, "these are .*") // c.Assert(net.ParseIP("1.2.3.4"), qt.Matches, "1.*") // var Matches Checker = &matchesChecker{ argNames: []string{"got value", "regexp"}, } type matchesChecker struct { argNames } // Check implements Checker.Check by checking that got is a string or a // fmt.Stringer and that it matches args[0]. func (c *matchesChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { pattern := args[0] switch v := got.(type) { case string: return match(v, pattern, "value does not match regexp", note) case fmt.Stringer: return match(v.String(), pattern, "value.String() does not match regexp", note) } note("value", got) return BadCheckf("value is not a string or a fmt.Stringer") } func checkFirstArgIsError(got interface{}, note func(key string, value interface{})) error { if got == nil { return errors.New("got nil error but want non-nil") } _, ok := got.(error) if !ok { note("got", got) return BadCheckf("first argument is not an error") } return nil } // ErrorMatches is a Checker checking that the provided value is an error whose // message matches the provided regular expression pattern. // // For instance: // // c.Assert(err, qt.ErrorMatches, "bad wolf .*") // var ErrorMatches Checker = &errorMatchesChecker{ argNames: []string{"got error", "regexp"}, } type errorMatchesChecker struct { argNames } // Check implements Checker.Check by checking that got is an error whose // Error() matches args[0]. func (c *errorMatchesChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { if err := checkFirstArgIsError(got, note); err != nil { return err } gotErr := got.(error) return match(gotErr.Error(), args[0], "error does not match regexp", note) } // PanicMatches is a Checker checking that the provided function panics with a // message matching the provided regular expression pattern. // // For instance: // // c.Assert(func() {panic("bad wolf ...")}, qt.PanicMatches, "bad wolf .*") // var PanicMatches Checker = &panicMatchesChecker{ argNames: []string{"function", "regexp"}, } type panicMatchesChecker struct { argNames } // Check implements Checker.Check by checking that got is a func() that panics // with a message matching args[0]. func (c *panicMatchesChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { f := reflect.ValueOf(got) if f.Kind() != reflect.Func { note("got", got) return BadCheckf("first argument is not a function") } ftype := f.Type() if ftype.NumIn() != 0 { note("function", got) return BadCheckf("cannot use a function receiving arguments") } defer func() { r := recover() if r == nil { err = errors.New("function did not panic") return } msg := fmt.Sprint(r) note("panic value", msg) err = match(msg, args[0], "panic value does not match regexp", note) }() f.Call(nil) return nil } // IsNil is a Checker checking that the provided value is nil. // // For instance: // // c.Assert(got, qt.IsNil) // // As a special case, if the value is nil but implements the // error interface, it is still considered to be non-nil. // This means that IsNil will fail on an error value that happens // to have an underlying nil value, because that's // invariably a mistake. // See https://golang.org/doc/faq#nil_error. var IsNil Checker = &isNilChecker{ argNames: []string{"got"}, } type isNilChecker struct { argNames } // Check implements Checker.Check by checking that got is nil. func (c *isNilChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { if got == nil { return nil } value := reflect.ValueOf(got) _, isError := got.(error) if canBeNil(value.Kind()) && value.IsNil() { if isError { // It's an error with an underlying nil value. return fmt.Errorf("error containing nil value of type %T. See https://golang.org/doc/faq#nil_error", got) } return nil } if isError { return errors.New("got non-nil error") } return errors.New("got non-nil value") } // IsNotNil is a Checker checking that the provided value is not nil. // IsNotNil is the equivalent of qt.Not(qt.IsNil) // // For instance: // // c.Assert(got, qt.IsNotNil) // var IsNotNil Checker = ¬Checker{ Checker: IsNil, } // HasLen is a Checker checking that the provided value has the given length. // // For instance: // // c.Assert([]int{42, 47}, qt.HasLen, 2) // c.Assert(myMap, qt.HasLen, 42) // var HasLen Checker = &hasLenChecker{ argNames: []string{"got", "want length"}, } type hasLenChecker struct { argNames } // Check implements Checker.Check by checking that len(got) == args[0]. func (c *hasLenChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { v := reflect.ValueOf(got) switch v.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: default: note("got", got) return BadCheckf("first argument has no length") } want, ok := args[0].(int) if !ok { note("length", args[0]) return BadCheckf("length is not an int") } length := v.Len() note("len(got)", length) if length != want { return fmt.Errorf("unexpected length") } return nil } // Implements checks that the provided value implements an interface. The // interface is specified with a pointer to an interface variable. // // For instance: // // var rc io.ReadCloser // c.Assert(myReader, qt.Implements, &rc) // var Implements Checker = &implementsChecker{ argNames: []string{"got", "want interface pointer"}, } type implementsChecker struct { argNames } var emptyInterface = reflect.TypeOf((*interface{})(nil)).Elem() // Check implements Checker.Check by checking that got implements the // interface pointed to by args[0]. func (c *implementsChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { if got == nil { note("error", Unquoted("got nil value but want non-nil")) note("got", got) return ErrSilent } if args[0] == nil { return BadCheckf("want a pointer to an interface variable but nil was provided") } wantType := reflect.TypeOf(args[0]) if wantType.Kind() != reflect.Ptr { note("want", Unquoted(wantType.String())) return BadCheckf("want a pointer to an interface variable but a non-pointer value was provided") } else if wantType.Elem().Kind() != reflect.Interface { note("want pointer type", Unquoted(wantType.Elem().String())) return BadCheckf("want a pointer to an interface variable but a pointer to a concrete type was provided") } else if wantType.Elem() == emptyInterface { note("want pointer type", Unquoted(wantType.Elem().String())) return BadCheckf("all types implement the empty interface, want a pointer to a variable that isn't the empty interface") } gotType := reflect.TypeOf(got) if !gotType.Implements(wantType.Elem()) { note("error", Unquoted("got value does not implement wanted interface")) note("got", got) note("want interface", Unquoted(wantType.Elem().String())) return ErrSilent } return nil } // Satisfies is a Checker checking that the provided value, when used as // argument of the provided predicate function, causes the function to return // true. The function must be of type func(T) bool, having got assignable to T. // // For instance: // // // Check that an error from os.Open satisfies os.IsNotExist. // c.Assert(err, qt.Satisfies, os.IsNotExist) // // // Check that a floating point number is a not-a-number. // c.Assert(f, qt.Satisfies, math.IsNaN) // var Satisfies Checker = &satisfiesChecker{ argNames: []string{"arg", "predicate function"}, } type satisfiesChecker struct { argNames } // Check implements Checker.Check by checking that args[0](got) == true. func (c *satisfiesChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { // Original code at // . // Copyright 2011 Canonical Ltd. // Licensed under the LGPLv3, see LICENCE file for details. predicate := args[0] f := reflect.ValueOf(predicate) ftype := f.Type() if ftype.Kind() != reflect.Func || ftype.NumIn() != 1 || ftype.NumOut() != 1 || ftype.Out(0).Kind() != reflect.Bool { note("predicate function", predicate) return BadCheckf("predicate function is not a func(T) bool") } v, t := reflect.ValueOf(got), ftype.In(0) if !v.IsValid() { if !canBeNil(t.Kind()) { note("predicate function", predicate) return BadCheckf("cannot use nil as type %v in argument to predicate function", t) } v = reflect.Zero(t) } else if !v.Type().AssignableTo(t) { note("arg", got) note("predicate function", predicate) return BadCheckf("cannot use value of type %v as type %v in argument to predicate function", v.Type(), t) } if f.Call([]reflect.Value{v})[0].Interface().(bool) { return nil } return fmt.Errorf("value does not satisfy predicate function") } // IsTrue is a Checker checking that the provided value is true. // The value must have a boolean underlying type. // // For instance: // // c.Assert(true, qt.IsTrue) // c.Assert(myBoolean(false), qt.IsTrue) // var IsTrue Checker = &boolChecker{ want: true, } // IsFalse is a Checker checking that the provided value is false. // The value must have a boolean underlying type. // // For instance: // // c.Assert(false, qt.IsFalse) // c.Assert(IsValid(), qt.IsFalse) // var IsFalse Checker = &boolChecker{ want: false, } type boolChecker struct { want bool } // Check implements Checker.Check by checking that got == c.want. func (c *boolChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { v := reflect.ValueOf(got) if v.IsValid() && v.Kind() == reflect.Bool { if v.Bool() != c.want { return fmt.Errorf("value is not %v", c.want) } return nil } note("value", got) return BadCheckf("value does not have a bool underlying type") } // ArgNames implements Checker.ArgNames. func (c *boolChecker) ArgNames() []string { return []string{"got"} } // Not returns a Checker negating the given Checker. // // For instance: // // c.Assert(got, qt.Not(qt.IsNil)) // c.Assert(answer, qt.Not(qt.Equals), 42) // func Not(checker Checker) Checker { return ¬Checker{ Checker: checker, } } type notChecker struct { Checker } // Check implements Checker.Check by checking that the stored checker fails. func (c *notChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { if nc, ok := c.Checker.(*notChecker); ok { return nc.Checker.Check(got, args, note) } err = c.Checker.Check(got, args, note) if IsBadCheck(err) { return err } if err != nil { return nil } return errors.New("unexpected success") } // Contains is a checker that checks that a map, slice, array // or string contains a value. It's the same as using // Any(Equals), except that it has a special case // for strings - if the first argument is a string, // the second argument must also be a string // and strings.Contains will be used. // // For example: // // c.Assert("hello world", qt.Contains, "world") // c.Assert([]int{3,5,7,99}, qt.Contains, 7) // var Contains Checker = &containsChecker{ argNames: []string{"container", "want"}, } type containsChecker struct { argNames } // Check implements Checker.Check by checking that got contains args[0]. func (c *containsChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { if got, ok := got.(string); ok { want, ok := args[0].(string) if !ok { return BadCheckf("strings can only contain strings, not %T", args[0]) } if strings.Contains(got, want) { return nil } return errors.New("no substring match found") } return Any(Equals).Check(got, args, note) } // Any returns a Checker that uses the given checker to check elements // of a slice or array or the values from a map. It succeeds if any element // passes the check. // // For example: // // c.Assert([]int{3,5,7,99}, qt.Any(qt.Equals), 7) // c.Assert([][]string{{"a", "b"}, {"c", "d"}}, qt.Any(qt.DeepEquals), []string{"c", "d"}) // // See also All and Contains. func Any(c Checker) Checker { return &anyChecker{ argNames: append([]string{"container"}, c.ArgNames()[1:]...), elemChecker: c, } } type anyChecker struct { argNames elemChecker Checker } // Check implements Checker.Check by checking that one of the elements of // got passes the c.elemChecker check. func (c *anyChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { iter, err := newIter(got) if err != nil { return BadCheckf("%v", err) } for iter.next() { // For the time being, discard the notes added by the sub-checker, // because it's not clear what a good behaviour would be. // Should we print all the failed check for all elements? If there's only // one element in the container, the answer is probably yes, // but let's leave it for now. err := c.elemChecker.Check( iter.value().Interface(), args, func(key string, value interface{}) {}, ) if err == nil { return nil } if IsBadCheck(err) { return BadCheckf("at %s: %v", iter.key(), err) } } return errors.New("no matching element found") } // All returns a Checker that uses the given checker to check elements // of slice or array or the values of a map. It succeeds if all elements // pass the check. // On failure it prints the error from the first index that failed. // // For example: // // c.Assert([]int{3, 5, 8}, qt.All(qt.Not(qt.Equals)), 0) // c.Assert([][]string{{"a", "b"}, {"a", "b"}}, qt.All(qt.DeepEquals), []string{"c", "d"}) // // See also Any and Contains. func All(c Checker) Checker { return &allChecker{ argNames: append([]string{"container"}, c.ArgNames()[1:]...), elemChecker: c, } } type allChecker struct { argNames elemChecker Checker } // Check implement Checker.Check by checking that all the elements of got // pass the c.elemChecker check. func (c *allChecker) Check(got interface{}, args []interface{}, notef func(key string, value interface{})) error { iter, err := newIter(got) if err != nil { return BadCheckf("%v", err) } for iter.next() { // Store any notes added by the checker so // we can add our own note at the start // to say which element failed. var notes []note err := c.elemChecker.Check( iter.value().Interface(), args, func(key string, val interface{}) { notes = append(notes, note{key, val}) }, ) if err == nil { continue } if IsBadCheck(err) { return BadCheckf("at %s: %v", iter.key(), err) } notef("error", Unquoted("mismatch at "+iter.key())) // TODO should we print the whole container value in // verbose mode? if err != ErrSilent { // If the error's not silent, the checker is expecting // the caller to print the error and the value that failed. notef("error", Unquoted(err.Error())) notef("first mismatched element", iter.value().Interface()) } for _, n := range notes { notef(n.key, n.value) } return ErrSilent } return nil } // JSONEquals is a checker that checks whether a byte slice // or string is JSON-equivalent to a Go value. See CodecEquals for // more information. // // It uses DeepEquals to do the comparison. If a more sophisticated // comparison is required, use CodecEquals directly. // // For instance: // // c.Assert(`{"First": 47.11}`, qt.JSONEquals, &MyStruct{First: 47.11}) // var JSONEquals = CodecEquals(json.Marshal, json.Unmarshal) type codecEqualChecker struct { argNames marshal func(interface{}) ([]byte, error) unmarshal func([]byte, interface{}) error deepEquals Checker } // CodecEquals returns a checker that checks for codec value equivalence. // // It expects two arguments: a byte slice or a string containing some // codec-marshaled data, and a Go value. // // It uses unmarshal to unmarshal the data into an interface{} value. // It marshals the Go value using marshal, then unmarshals the result into // an interface{} value. // // It then checks that the two interface{} values are deep-equal to one // another, using CmpEquals(opts) to perform the check. // // See JSONEquals for an example of this in use. func CodecEquals( marshal func(interface{}) ([]byte, error), unmarshal func([]byte, interface{}) error, opts ...cmp.Option, ) Checker { return &codecEqualChecker{ argNames: argNames{"got", "want"}, marshal: marshal, unmarshal: unmarshal, deepEquals: CmpEquals(opts...), } } func (c *codecEqualChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { var gotContent []byte switch got := got.(type) { case string: gotContent = []byte(got) case []byte: gotContent = got default: return BadCheckf("expected string or byte, got %T", got) } wantContent := args[0] wantContentBytes, err := c.marshal(wantContent) if err != nil { return BadCheckf("cannot marshal expected contents: %v", err) } var wantContentVal interface{} if err := c.unmarshal(wantContentBytes, &wantContentVal); err != nil { return BadCheckf("cannot unmarshal expected contents: %v", err) } var gotContentVal interface{} if err := c.unmarshal([]byte(gotContent), &gotContentVal); err != nil { return fmt.Errorf("cannot unmarshal obtained contents: %v; %q", err, gotContent) } return c.deepEquals.Check(gotContentVal, []interface{}{wantContentVal}, note) } // argNames helps implementing Checker.ArgNames. type argNames []string // ArgNames implements Checker.ArgNames by returning the argument names. func (a argNames) ArgNames() []string { return a } // match checks that the given error message matches the given pattern. func match(got string, pattern interface{}, msg string, note func(key string, value interface{})) error { regex, ok := pattern.(string) if !ok { note("regexp", pattern) return BadCheckf("regexp is not a string") } matches, err := regexp.MatchString("^("+regex+")$", got) if err != nil { note("regexp", regex) return BadCheckf("cannot compile regexp: %s", err) } if matches { return nil } return errors.New(msg) } // canBeNil reports whether a value or type of the given kind can be nil. func canBeNil(k reflect.Kind) bool { switch k { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: return true } return false } quicktest-1.14.0/checker_go1.13.go000066400000000000000000000043151413325465400165460ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build go1.13 // +build go1.13 package quicktest import ( "errors" ) // ErrorAs checks that the error is or wraps a specific error type. If so, it // assigns it to the provided pointer. This is analogous to calling errors.As. // // For instance: // // // Checking for a specific error type // c.Assert(err, qt.ErrorAs, new(*os.PathError)) // // // Checking fields on a specific error type // var pathError *os.PathError // if c.Check(err, qt.ErrorAs, &pathError) { // c.Assert(pathError.Path, Equals, "some_path") // } // var ErrorAs Checker = &errorAsChecker{ argNames: []string{"got", "as"}, } type errorAsChecker struct { argNames } // Check implements Checker.Check by checking that got is an error whose error // chain matches args[0] and assigning it to args[0]. func (c *errorAsChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) (err error) { if err := checkFirstArgIsError(got, note); err != nil { return err } gotErr := got.(error) defer func() { // A panic is raised when the target is not a pointer to an interface // or error. if r := recover(); r != nil { err = BadCheckf("%s", r) } }() if !errors.As(gotErr, args[0]) { return errors.New("wanted type is not found in error chain") } return nil } // ErrorIs checks that the error is or wraps a specific error value. This is // analogous to calling errors.Is. // // For instance: // // c.Assert(err, qt.ErrorIs, os.ErrNotExist) // var ErrorIs Checker = &errorIsChecker{ argNames: []string{"got", "want"}, } type errorIsChecker struct { argNames } // Check implements Checker.Check by checking that got is an error whose error // chain matches args[0]. func (c *errorIsChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { if err := checkFirstArgIsError(got, note); err != nil { return err } gotErr := got.(error) wantErr, ok := args[0].(error) if !ok { note("want", args[0]) return BadCheckf("second argument is not an error") } if !errors.Is(gotErr, wantErr) { return errors.New("wanted error is not found in error chain") } return nil } quicktest-1.14.0/checker_go1.13_test.go000066400000000000000000000077071413325465400176150ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build go1.13 // +build go1.13 package quicktest_test import ( "errors" "fmt" qt "github.com/frankban/quicktest" ) func init() { checkerTests = append(checkerTests, errorCheckerTests...) } type errTarget struct { msg string } func (e *errTarget) Error() string { return e.msg } var ( targetErr = &errTarget{msg: "target"} ) var errorCheckerTests = []struct { about string checker qt.Checker got interface{} args []interface{} verbose bool expectedCheckFailure string expectedNegateFailure string }{{ about: "ErrorAs: exact match", checker: qt.ErrorAs, got: targetErr, args: []interface{}{new(*errTarget)}, expectedNegateFailure: ` error: unexpected success got: e"target" as: &&quicktest_test.errTarget{msg:"target"} `, }, { about: "ErrorAs: wrapped match", checker: qt.ErrorAs, got: fmt.Errorf("wrapped: %w", targetErr), args: []interface{}{new(*errTarget)}, expectedNegateFailure: ` error: unexpected success got: e"wrapped: target" as: &&quicktest_test.errTarget{msg:"target"} `, }, { about: "ErrorAs: fails if nil error", checker: qt.ErrorAs, got: nil, args: []interface{}{new(*errTarget)}, expectedCheckFailure: ` error: got nil error but want non-nil got: nil as: &(*quicktest_test.errTarget)(nil) `, }, { about: "ErrorAs: fails if mismatch", checker: qt.ErrorAs, got: errors.New("other error"), args: []interface{}{new(*errTarget)}, expectedCheckFailure: ` error: wanted type is not found in error chain got: e"other error" as: &(*quicktest_test.errTarget)(nil) `, }, { about: "ErrorAs: bad check if invalid error", checker: qt.ErrorAs, got: "not an error", args: []interface{}{new(*errTarget)}, expectedCheckFailure: ` error: bad check: first argument is not an error got: "not an error" `, expectedNegateFailure: ` error: bad check: first argument is not an error got: "not an error" `, }, { about: "ErrorAs: bad check if invalid as", checker: qt.ErrorAs, got: targetErr, args: []interface{}{&struct{}{}}, expectedCheckFailure: ` error: bad check: errors: *target must be interface or implement error `, expectedNegateFailure: ` error: bad check: errors: *target must be interface or implement error `, }, { about: "ErrorIs: exact match", checker: qt.ErrorIs, got: targetErr, args: []interface{}{targetErr}, expectedNegateFailure: ` error: unexpected success got: e"target" want: `, }, { about: "ErrorIs: wrapped match", checker: qt.ErrorIs, got: fmt.Errorf("wrapped: %w", targetErr), args: []interface{}{targetErr}, expectedNegateFailure: ` error: unexpected success got: e"wrapped: target" want: e"target" `, }, { about: "ErrorIs: fails if nil error", checker: qt.ErrorIs, got: nil, args: []interface{}{targetErr}, expectedCheckFailure: ` error: got nil error but want non-nil got: nil want: e"target" `, }, { about: "ErrorIs: fails if mismatch", checker: qt.ErrorIs, got: errors.New("other error"), args: []interface{}{targetErr}, expectedCheckFailure: ` error: wanted error is not found in error chain got: e"other error" want: e"target" `, }, { about: "ErrorIs: bad check if invalid error", checker: qt.ErrorIs, got: "not an error", args: []interface{}{targetErr}, expectedCheckFailure: ` error: bad check: first argument is not an error got: "not an error" `, expectedNegateFailure: ` error: bad check: first argument is not an error got: "not an error" `, }, { about: "ErrorIs: bad check if invalid error value", checker: qt.ErrorIs, got: targetErr, args: []interface{}{"not an error"}, expectedCheckFailure: ` error: bad check: second argument is not an error want: "not an error" `, expectedNegateFailure: ` error: bad check: second argument is not an error want: "not an error" `, }} quicktest-1.14.0/checker_test.go000066400000000000000000001570741413325465400166300ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "bytes" "encoding/json" "errors" "fmt" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" qt "github.com/frankban/quicktest" ) // Fooer is an interface for testing. type Fooer interface { Foo() } var ( goTime = time.Date(2012, 3, 28, 0, 0, 0, 0, time.UTC) chInt = func() chan int { ch := make(chan int, 4) ch <- 42 ch <- 47 return ch }() sameInts = cmpopts.SortSlices(func(x, y int) bool { return x < y }) cmpEqualsGot = struct { Strings []interface{} Ints []int }{ Strings: []interface{}{"who", "dalek"}, Ints: []int{42, 47}, } cmpEqualsWant = struct { Strings []interface{} Ints []int }{ Strings: []interface{}{"who", "dalek"}, Ints: []int{42}, } ) type InnerJSON struct { First string Second int `json:",omitempty" yaml:",omitempty"` Third map[string]bool `json:",omitempty" yaml:",omitempty"` } type OuterJSON struct { First float64 Second []*InnerJSON `json:"Last,omitempty" yaml:"last,omitempty"` } type boolean bool var checkerTests = []struct { about string checker qt.Checker got interface{} args []interface{} verbose bool expectedCheckFailure string expectedNegateFailure string }{{ about: "Equals: same values", checker: qt.Equals, got: 42, args: []interface{}{42}, expectedNegateFailure: ` error: unexpected success got: int(42) want: `, }, { about: "Equals: different values", checker: qt.Equals, got: "42", args: []interface{}{"47"}, expectedCheckFailure: ` error: values are not equal got: "42" want: "47" `, }, { about: "Equals: different strings with quotes", checker: qt.Equals, got: `string "foo"`, args: []interface{}{`string "bar"`}, expectedCheckFailure: tilde2bq(` error: values are not equal got: ~string "foo"~ want: ~string "bar"~ `), }, { about: "Equals: same multiline strings", checker: qt.Equals, got: "a\nmultiline\nstring", args: []interface{}{"a\nmultiline\nstring"}, expectedNegateFailure: ` error: unexpected success got: "a\nmultiline\nstring" want: `, }, { about: "Equals: different multi-line strings", checker: qt.Equals, got: "a\nlong\nmultiline\nstring", args: []interface{}{"just\na\nlong\nmulti-line\nstring\n"}, expectedCheckFailure: fmt.Sprintf(` error: values are not equal line diff (-got +want): %s got: "a\nlong\nmultiline\nstring" want: "just\na\nlong\nmulti-line\nstring\n" `, diff([]string{"a\n", "long\n", "multiline\n", "string"}, []string{"just\n", "a\n", "long\n", "multi-line\n", "string\n", ""})), }, { about: "Equals: different single-line strings ending with newline", checker: qt.Equals, got: "foo\n", args: []interface{}{"bar\n"}, expectedCheckFailure: ` error: values are not equal got: "foo\n" want: "bar\n" `, }, { about: "Equals: different strings starting with newline", checker: qt.Equals, got: "\nfoo", args: []interface{}{"\nbar"}, expectedCheckFailure: fmt.Sprintf(` error: values are not equal line diff (-got +want): %s got: "\nfoo" want: "\nbar" `, diff([]string{"\n", "foo"}, []string{"\n", "bar"})), }, { about: "Equals: different types", checker: qt.Equals, got: 42, args: []interface{}{"42"}, expectedCheckFailure: ` error: values are not equal got: int(42) want: "42" `, }, { about: "Equals: nil and nil", checker: qt.Equals, got: nil, args: []interface{}{nil}, expectedNegateFailure: ` error: unexpected success got: nil want: `, }, { about: "Equals: error is not nil", checker: qt.Equals, got: errBadWolf, args: []interface{}{nil}, expectedCheckFailure: ` error: got non-nil error got: bad wolf file:line want: nil `, }, { about: "Equals: error is not nil: not formatted", checker: qt.Equals, got: &errTest{ msg: "bad wolf", }, args: []interface{}{nil}, expectedCheckFailure: ` error: got non-nil error got: e"bad wolf" want: nil `, }, { about: "Equals: error does not guard against nil", checker: qt.Equals, got: (*errTest)(nil), args: []interface{}{nil}, expectedCheckFailure: ` error: got non-nil error got: e want: nil `, }, { about: "Equals: error is not nil: not formatted and with quotes", checker: qt.Equals, got: &errTest{ msg: `failure: "bad wolf"`, }, args: []interface{}{nil}, expectedCheckFailure: tilde2bq(` error: got non-nil error got: e~failure: "bad wolf"~ want: nil `), }, { about: "Equals: different errors with same message", checker: qt.Equals, got: &errTest{ msg: "bad wolf", }, args: []interface{}{errors.New("bad wolf")}, expectedCheckFailure: ` error: values are not equal got type: *quicktest_test.errTest want type: *errors.errorString got: e"bad wolf" want: `, }, { about: "Equals: nil struct", checker: qt.Equals, got: (*struct{})(nil), args: []interface{}{nil}, expectedCheckFailure: ` error: values are not equal got: (*struct {})(nil) want: nil `, }, { about: "Equals: different booleans", checker: qt.Equals, got: true, args: []interface{}{false}, expectedCheckFailure: ` error: values are not equal got: bool(true) want: bool(false) `, }, { about: "Equals: uncomparable types", checker: qt.Equals, got: struct { Ints []int }{ Ints: []int{42, 47}, }, args: []interface{}{struct { Ints []int }{ Ints: []int{42, 47}, }}, expectedCheckFailure: ` error: runtime error: comparing uncomparable type struct { Ints []int } got: struct { Ints []int }{ Ints: {42, 47}, } want: `, }, { about: "Equals: not enough arguments", checker: qt.Equals, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, }, { about: "Equals: too many arguments", checker: qt.Equals, args: []interface{}{nil, 47}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ nil, int(47), } want args: want `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ nil, int(47), } want args: want `, }, { about: "CmpEquals: same values", checker: qt.CmpEquals(), got: cmpEqualsGot, args: []interface{}{cmpEqualsGot}, expectedNegateFailure: ` error: unexpected success got: struct { Strings []interface {}; Ints []int }{ Strings: { "who", "dalek", }, Ints: {42, 47}, } want: `, }, { about: "CmpEquals: different values", checker: qt.CmpEquals(), got: cmpEqualsGot, args: []interface{}{cmpEqualsWant}, expectedCheckFailure: fmt.Sprintf(` error: values are not deep equal diff (-got +want): %s `, diff(cmpEqualsGot, cmpEqualsWant)), }, { about: "CmpEquals: different values: verbose", checker: qt.CmpEquals(), 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: "CmpEquals: same values with options", checker: qt.CmpEquals(sameInts), got: []int{1, 2, 3}, args: []interface{}{ []int{3, 2, 1}, }, expectedNegateFailure: ` error: unexpected success got: []int{1, 2, 3} want: []int{3, 2, 1} `, }, { about: "CmpEquals: different values with options", checker: qt.CmpEquals(sameInts), got: []int{1, 2, 4}, args: []interface{}{ []int{3, 2, 1}, }, expectedCheckFailure: fmt.Sprintf(` error: values are not deep equal diff (-got +want): %s `, diff([]int{1, 2, 4}, []int{3, 2, 1}, sameInts)), }, { about: "CmpEquals: different values with options: verbose", checker: qt.CmpEquals(sameInts), got: []int{1, 2, 4}, args: []interface{}{ []int{3, 2, 1}, }, verbose: true, expectedCheckFailure: fmt.Sprintf(` error: values are not deep equal diff (-got +want): %s got: []int{1, 2, 4} want: []int{3, 2, 1} `, diff([]int{1, 2, 4}, []int{3, 2, 1}, sameInts)), }, { about: "CmpEquals: structs with unexported fields not allowed", checker: qt.CmpEquals(), got: struct{ answer int }{ answer: 42, }, args: []interface{}{ struct{ answer int }{ answer: 42, }, }, expectedCheckFailure: ` error: cannot handle unexported field at root.answer: "github.com/frankban/quicktest_test".(struct { answer int }) consider using a custom Comparer; if you control the implementation of type, you can also consider using an Exporter, AllowUnexported, or cmpopts.IgnoreUnexported got: struct { answer int }{answer:42} want: `, }, { about: "CmpEquals: structs with unexported fields ignored", checker: qt.CmpEquals(cmpopts.IgnoreUnexported(struct{ answer int }{})), got: struct{ answer int }{ answer: 42, }, args: []interface{}{ struct{ answer int }{ answer: 42, }, }, expectedNegateFailure: ` error: unexpected success got: struct { answer int }{answer:42} want: `, }, { about: "CmpEquals: same times", checker: qt.CmpEquals(), got: goTime, args: []interface{}{ goTime, }, expectedNegateFailure: ` error: unexpected success got: s"2012-03-28 00:00:00 +0000 UTC" want: `, }, { about: "CmpEquals: different times: verbose", checker: qt.CmpEquals(), got: goTime.Add(24 * time.Hour), args: []interface{}{ goTime, }, verbose: true, expectedCheckFailure: fmt.Sprintf(` error: values are not deep equal diff (-got +want): %s got: s"2012-03-29 00:00:00 +0000 UTC" want: s"2012-03-28 00:00:00 +0000 UTC" `, diff(goTime.Add(24*time.Hour), goTime)), }, { about: "CmpEquals: not enough arguments", checker: qt.CmpEquals(), expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, }, { about: "CmpEquals: too many arguments", checker: qt.CmpEquals(), got: []int{42}, args: []interface{}{[]int{42}, "bad wolf"}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ []int{42}, "bad wolf", } want args: want `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ []int{42}, "bad wolf", } want args: 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", checker: qt.ContentEquals, got: []string{"these", "are", "the", "voyages"}, args: []interface{}{ []string{"these", "are", "the", "voyages"}, }, expectedNegateFailure: ` error: unexpected success got: []string{"these", "are", "the", "voyages"} want: `, }, { about: "ContentEquals: same contents", checker: qt.ContentEquals, got: []int{1, 2, 3}, args: []interface{}{ []int{3, 2, 1}, }, expectedNegateFailure: ` error: unexpected success got: []int{1, 2, 3} want: []int{3, 2, 1} `, }, { about: "ContentEquals: same contents on complex slice", checker: qt.ContentEquals, got: []struct { Strings []interface{} Ints []int }{cmpEqualsGot, cmpEqualsGot, cmpEqualsWant}, args: []interface{}{ []struct { Strings []interface{} Ints []int }{cmpEqualsWant, cmpEqualsGot, cmpEqualsGot}, }, expectedNegateFailure: ` error: unexpected success got: []struct { Strings []interface {}; Ints []int }{ { Strings: { "who", "dalek", }, Ints: {42, 47}, }, { Strings: { "who", "dalek", }, Ints: {42, 47}, }, { Strings: { "who", "dalek", }, Ints: {42}, }, } want: []struct { Strings []interface {}; Ints []int }{ { Strings: { "who", "dalek", }, Ints: {42}, }, { Strings: { "who", "dalek", }, Ints: {42, 47}, }, { Strings: { "who", "dalek", }, Ints: {42, 47}, }, } `, }, { about: "ContentEquals: same contents on a nested slice", checker: qt.ContentEquals, got: struct { Nums []int }{ Nums: []int{1, 2, 3, 4}, }, args: []interface{}{ struct { Nums []int }{ Nums: []int{4, 3, 2, 1}, }, }, expectedNegateFailure: ` error: unexpected success got: struct { Nums []int }{ Nums: {1, 2, 3, 4}, } want: struct { Nums []int }{ Nums: {4, 3, 2, 1}, } `, }, { about: "ContentEquals: slices of different type", checker: qt.ContentEquals, got: []string{"bad", "wolf"}, args: []interface{}{ []interface{}{"bad", "wolf"}, }, expectedCheckFailure: fmt.Sprintf(` error: values are not deep equal diff (-got +want): %s `, diff([]string{"bad", "wolf"}, []interface{}{"bad", "wolf"})), }, { about: "ContentEquals: not enough arguments", checker: qt.ContentEquals, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, }, { about: "ContentEquals: too many arguments", checker: qt.ContentEquals, args: []interface{}{nil, nil}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ nil, nil, } want args: want `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ nil, nil, } want args: want `, }, { about: "Matches: perfect match", checker: qt.Matches, got: "exterminate", args: []interface{}{"exterminate"}, expectedNegateFailure: ` error: unexpected success got value: "exterminate" regexp: `, }, { about: "Matches: match", checker: qt.Matches, got: "these are the voyages", args: []interface{}{"these are the .*"}, expectedNegateFailure: ` error: unexpected success got value: "these are the voyages" regexp: "these are the .*" `, }, { about: "Matches: match with stringer", checker: qt.Matches, got: bytes.NewBufferString("resistance is futile"), args: []interface{}{"resistance is (futile|useful)"}, expectedNegateFailure: ` error: unexpected success got value: s"resistance is futile" regexp: "resistance is (futile|useful)" `, }, { about: "Matches: mismatch", checker: qt.Matches, got: "voyages", args: []interface{}{"these are the voyages"}, expectedCheckFailure: ` error: value does not match regexp got value: "voyages" regexp: "these are the voyages" `, }, { about: "Matches: mismatch with stringer", checker: qt.Matches, got: bytes.NewBufferString("voyages"), args: []interface{}{"these are the voyages"}, expectedCheckFailure: ` error: value.String() does not match regexp got value: s"voyages" regexp: "these are the voyages" `, }, { about: "Matches: empty pattern", checker: qt.Matches, got: "these are the voyages", args: []interface{}{""}, expectedCheckFailure: ` error: value does not match regexp got value: "these are the voyages" regexp: "" `, }, { about: "Matches: complex pattern", checker: qt.Matches, got: "end of the universe", args: []interface{}{"bad wolf|end of the .*"}, expectedNegateFailure: ` error: unexpected success got value: "end of the universe" regexp: "bad wolf|end of the .*" `, }, { about: "Matches: invalid pattern", checker: qt.Matches, got: "voyages", args: []interface{}{"("}, expectedCheckFailure: ` error: bad check: cannot compile regexp: error parsing regexp: missing closing ): ` + "`^(()$`" + ` regexp: "(" `, expectedNegateFailure: ` error: bad check: cannot compile regexp: error parsing regexp: missing closing ): ` + "`^(()$`" + ` regexp: "(" `, }, { about: "Matches: pattern not a string", checker: qt.Matches, got: "", args: []interface{}{[]int{42}}, expectedCheckFailure: ` error: bad check: regexp is not a string regexp: []int{42} `, expectedNegateFailure: ` error: bad check: regexp is not a string regexp: []int{42} `, }, { about: "Matches: not a string or as stringer", checker: qt.Matches, got: 42, args: []interface{}{".*"}, expectedCheckFailure: ` error: bad check: value is not a string or a fmt.Stringer value: int(42) `, expectedNegateFailure: ` error: bad check: value is not a string or a fmt.Stringer value: int(42) `, }, { about: "Matches: not enough arguments", checker: qt.Matches, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, }, { about: "Matches: too many arguments", checker: qt.Matches, got: "these are the voyages", args: []interface{}{"these are the .*", nil}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ "these are the .*", nil, } want args: regexp `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ "these are the .*", nil, } want args: regexp `, }, { about: "ErrorMatches: perfect match", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{"bad wolf"}, expectedNegateFailure: ` error: unexpected success got error: bad wolf file:line regexp: "bad wolf" `, }, { about: "ErrorMatches: match", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{"bad .*"}, expectedNegateFailure: ` error: unexpected success got error: bad wolf file:line regexp: "bad .*" `, }, { about: "ErrorMatches: mismatch", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{"exterminate"}, expectedCheckFailure: ` error: error does not match regexp got error: bad wolf file:line regexp: "exterminate" `, }, { about: "ErrorMatches: empty pattern", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{""}, expectedCheckFailure: ` error: error does not match regexp got error: bad wolf file:line regexp: "" `, }, { about: "ErrorMatches: complex pattern", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{"bad wolf|end of the universe"}, expectedNegateFailure: ` error: unexpected success got error: bad wolf file:line regexp: "bad wolf|end of the universe" `, }, { about: "ErrorMatches: invalid pattern", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{"("}, expectedCheckFailure: ` error: bad check: cannot compile regexp: error parsing regexp: missing closing ): ` + "`^(()$`" + ` regexp: "(" `, expectedNegateFailure: ` error: bad check: cannot compile regexp: error parsing regexp: missing closing ): ` + "`^(()$`" + ` regexp: "(" `, }, { about: "ErrorMatches: pattern not a string", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{[]int{42}}, expectedCheckFailure: ` error: bad check: regexp is not a string regexp: []int{42} `, expectedNegateFailure: ` error: bad check: regexp is not a string regexp: []int{42} `, }, { about: "ErrorMatches: not an error", checker: qt.ErrorMatches, got: 42, args: []interface{}{".*"}, expectedCheckFailure: ` error: bad check: first argument is not an error got: int(42) `, expectedNegateFailure: ` error: bad check: first argument is not an error got: int(42) `, }, { about: "ErrorMatches: nil error", checker: qt.ErrorMatches, got: nil, args: []interface{}{"some pattern"}, expectedCheckFailure: ` error: got nil error but want non-nil got error: nil regexp: "some pattern" `, }, { about: "ErrorMatches: not enough arguments", checker: qt.ErrorMatches, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, }, { about: "ErrorMatches: too many arguments", checker: qt.ErrorMatches, got: errBadWolf, args: []interface{}{"bad wolf", []string{"bad", "wolf"}}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ "bad wolf", []string{"bad", "wolf"}, } want args: regexp `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ "bad wolf", []string{"bad", "wolf"}, } want args: regexp `, }, { about: "PanicMatches: perfect match", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{"error: bad wolf"}, expectedNegateFailure: ` error: unexpected success panic value: "error: bad wolf" function: func() {...} regexp: `, }, { about: "PanicMatches: match", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{"error: .*"}, expectedNegateFailure: ` error: unexpected success panic value: "error: bad wolf" function: func() {...} regexp: "error: .*" `, }, { about: "PanicMatches: mismatch", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{"error: exterminate"}, expectedCheckFailure: ` error: panic value does not match regexp panic value: "error: bad wolf" function: func() {...} regexp: "error: exterminate" `, }, { about: "PanicMatches: empty pattern", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{""}, expectedCheckFailure: ` error: panic value does not match regexp panic value: "error: bad wolf" function: func() {...} regexp: "" `, }, { about: "PanicMatches: complex pattern", checker: qt.PanicMatches, got: func() { panic("bad wolf") }, args: []interface{}{"bad wolf|end of the universe"}, expectedNegateFailure: ` error: unexpected success panic value: "bad wolf" function: func() {...} regexp: "bad wolf|end of the universe" `, }, { about: "PanicMatches: invalid pattern", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{"("}, expectedCheckFailure: ` error: bad check: cannot compile regexp: error parsing regexp: missing closing ): ` + "`^(()$`" + ` panic value: "error: bad wolf" regexp: "(" `, expectedNegateFailure: ` error: bad check: cannot compile regexp: error parsing regexp: missing closing ): ` + "`^(()$`" + ` panic value: "error: bad wolf" regexp: "(" `, }, { about: "PanicMatches: pattern not a string", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{nil}, expectedCheckFailure: ` error: bad check: regexp is not a string panic value: "error: bad wolf" regexp: nil `, expectedNegateFailure: ` error: bad check: regexp is not a string panic value: "error: bad wolf" regexp: nil `, }, { about: "PanicMatches: not a function", checker: qt.PanicMatches, got: map[string]int{"answer": 42}, args: []interface{}{".*"}, expectedCheckFailure: ` error: bad check: first argument is not a function got: map[string]int{"answer":42} `, expectedNegateFailure: ` error: bad check: first argument is not a function got: map[string]int{"answer":42} `, }, { about: "PanicMatches: not a proper function", checker: qt.PanicMatches, got: func(int) { panic("error: bad wolf") }, args: []interface{}{".*"}, expectedCheckFailure: ` error: bad check: cannot use a function receiving arguments function: func(int) {...} `, expectedNegateFailure: ` error: bad check: cannot use a function receiving arguments function: func(int) {...} `, }, { about: "PanicMatches: function returning something", checker: qt.PanicMatches, got: func() error { panic("error: bad wolf") }, args: []interface{}{".*"}, expectedNegateFailure: ` error: unexpected success panic value: "error: bad wolf" function: func() error {...} regexp: ".*" `, }, { about: "PanicMatches: no panic", checker: qt.PanicMatches, got: func() {}, args: []interface{}{".*"}, expectedCheckFailure: ` error: function did not panic function: func() {...} regexp: ".*" `, }, { about: "PanicMatches: not enough arguments", checker: qt.PanicMatches, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, }, { about: "PanicMatches: too many arguments", checker: qt.PanicMatches, got: func() { panic("error: bad wolf") }, args: []interface{}{"error: bad wolf", 42}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ "error: bad wolf", int(42), } want args: regexp `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ "error: bad wolf", int(42), } want args: regexp `, }, { about: "IsNil: nil", checker: qt.IsNil, got: nil, expectedNegateFailure: ` error: unexpected success got: nil `, }, { about: "IsNil: nil struct", checker: qt.IsNil, got: (*struct{})(nil), expectedNegateFailure: ` error: unexpected success got: (*struct {})(nil) `, }, { about: "IsNil: nil func", checker: qt.IsNil, got: (func())(nil), expectedNegateFailure: ` error: unexpected success got: func() {...} `, }, { about: "IsNil: nil map", checker: qt.IsNil, got: (map[string]string)(nil), expectedNegateFailure: ` error: unexpected success got: map[string]string{} `, }, { about: "IsNil: nil slice", checker: qt.IsNil, got: ([]int)(nil), expectedNegateFailure: ` error: unexpected success got: []int(nil) `, }, { about: "IsNil: nil error-implementing type", checker: qt.IsNil, got: (*errTest)(nil), expectedCheckFailure: ` error: error containing nil value of type *quicktest_test.errTest. See https://golang.org/doc/faq#nil_error got: e `, }, { about: "IsNil: not nil", checker: qt.IsNil, got: 42, expectedCheckFailure: ` error: got non-nil value got: int(42) `, }, { about: "IsNil: error is not nil", checker: qt.IsNil, got: errBadWolf, expectedCheckFailure: ` error: got non-nil error got: bad wolf file:line `, }, { about: "IsNil: too many arguments", checker: qt.IsNil, args: []interface{}{"not nil"}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 got args: []interface {}{ "not nil", } `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 got args: []interface {}{ "not nil", } `, }, { about: "IsNotNil: success", checker: qt.IsNotNil, got: 42, expectedNegateFailure: ` error: got non-nil value got: int(42) `, }, { about: "IsNotNil: failure", checker: qt.IsNotNil, got: nil, expectedCheckFailure: ` error: unexpected success got: nil `, }, { about: "HasLen: arrays with the same length", checker: qt.HasLen, got: [4]string{"these", "are", "the", "voyages"}, args: []interface{}{4}, expectedNegateFailure: ` error: unexpected success len(got): int(4) got: [4]string{"these", "are", "the", "voyages"} want length: `, }, { about: "HasLen: channels with the same length", checker: qt.HasLen, got: chInt, args: []interface{}{2}, expectedNegateFailure: fmt.Sprintf(` error: unexpected success len(got): int(2) got: (chan int)(%v) want length: `, chInt), }, { about: "HasLen: maps with the same length", checker: qt.HasLen, got: map[string]bool{"true": true}, args: []interface{}{1}, expectedNegateFailure: ` error: unexpected success len(got): int(1) got: map[string]bool{"true":true} want length: `, }, { about: "HasLen: slices with the same length", checker: qt.HasLen, got: []int{}, args: []interface{}{0}, expectedNegateFailure: ` error: unexpected success len(got): int(0) got: []int{} want length: `, }, { about: "HasLen: strings with the same length", checker: qt.HasLen, got: "these are the voyages", args: []interface{}{21}, expectedNegateFailure: ` error: unexpected success len(got): int(21) got: "these are the voyages" want length: `, }, { about: "HasLen: arrays with different lengths", checker: qt.HasLen, got: [4]string{"these", "are", "the", "voyages"}, args: []interface{}{0}, expectedCheckFailure: ` error: unexpected length len(got): int(4) got: [4]string{"these", "are", "the", "voyages"} want length: int(0) `, }, { about: "HasLen: channels with different lengths", checker: qt.HasLen, got: chInt, args: []interface{}{4}, expectedCheckFailure: fmt.Sprintf(` error: unexpected length len(got): int(2) got: (chan int)(%v) want length: int(4) `, chInt), }, { about: "HasLen: maps with different lengths", checker: qt.HasLen, got: map[string]bool{"true": true}, args: []interface{}{42}, expectedCheckFailure: ` error: unexpected length len(got): int(1) got: map[string]bool{"true":true} want length: int(42) `, }, { about: "HasLen: slices with different lengths", checker: qt.HasLen, got: []int{42, 47}, args: []interface{}{1}, expectedCheckFailure: ` error: unexpected length len(got): int(2) got: []int{42, 47} want length: int(1) `, }, { about: "HasLen: strings with different lengths", checker: qt.HasLen, got: "these are the voyages", args: []interface{}{42}, expectedCheckFailure: ` error: unexpected length len(got): int(21) got: "these are the voyages" want length: int(42) `, }, { about: "HasLen: value without a length", checker: qt.HasLen, got: 42, args: []interface{}{42}, expectedCheckFailure: ` error: bad check: first argument has no length got: int(42) `, expectedNegateFailure: ` error: bad check: first argument has no length got: int(42) `, }, { about: "HasLen: expected value not a number", checker: qt.HasLen, got: "these are the voyages", args: []interface{}{"bad wolf"}, expectedCheckFailure: ` error: bad check: length is not an int length: "bad wolf" `, expectedNegateFailure: ` error: bad check: length is not an int length: "bad wolf" `, }, { about: "HasLen: not enough arguments", checker: qt.HasLen, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want length `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want length `, }, { about: "HasLen: too many arguments", checker: qt.HasLen, got: []int{42}, args: []interface{}{42, 47}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ int(42), int(47), } want args: want length `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ int(42), int(47), } want args: want length `, }, { about: "Implements: implements interface", checker: qt.Implements, got: errBadWolf, args: []interface{}{(*error)(nil)}, expectedNegateFailure: ` error: unexpected success got: bad wolf file:line want interface pointer: (*error)(nil) `, }, { about: "Implements: does not implement interface", checker: qt.Implements, got: errBadWolf, args: []interface{}{(*Fooer)(nil)}, expectedCheckFailure: ` error: got value does not implement wanted interface got: bad wolf file:line want interface: quicktest_test.Fooer `, }, { about: "Implements: fails if got nil", checker: qt.Implements, got: nil, args: []interface{}{(*Fooer)(nil)}, expectedCheckFailure: ` error: got nil value but want non-nil got: nil `, }, { about: "Implements: bad check if wanted is nil", checker: qt.Implements, got: errBadWolf, args: []interface{}{nil}, expectedCheckFailure: ` error: bad check: want a pointer to an interface variable but nil was provided `, expectedNegateFailure: ` error: bad check: want a pointer to an interface variable but nil was provided `, }, { about: "Implements: bad check if wanted is not pointer", checker: qt.Implements, got: errBadWolf, args: []interface{}{struct{}{}}, expectedCheckFailure: ` error: bad check: want a pointer to an interface variable but a non-pointer value was provided want: struct {} `, expectedNegateFailure: ` error: bad check: want a pointer to an interface variable but a non-pointer value was provided want: struct {} `, }, { about: "Implements: bad check if wanted is not pointer to interface", checker: qt.Implements, got: errBadWolf, args: []interface{}{(*struct{})(nil)}, expectedCheckFailure: ` error: bad check: want a pointer to an interface variable but a pointer to a concrete type was provided want pointer type: struct {} `, expectedNegateFailure: ` error: bad check: want a pointer to an interface variable but a pointer to a concrete type was provided want pointer type: struct {} `, }, { about: "Implements: bad check if wanted is a pointer to the empty interface", checker: qt.Implements, got: 42, args: []interface{}{(*interface{})(nil)}, expectedCheckFailure: ` error: bad check: all types implement the empty interface, want a pointer to a variable that isn't the empty interface want pointer type: interface {} `, expectedNegateFailure: ` error: bad check: all types implement the empty interface, want a pointer to a variable that isn't the empty interface want pointer type: interface {} `, }, { about: "Satisfies: success with an error", checker: qt.Satisfies, got: qt.BadCheckf("bad wolf"), args: []interface{}{qt.IsBadCheck}, expectedNegateFailure: ` error: unexpected success arg: e"bad check: bad wolf" predicate function: func(error) bool {...} `, }, { about: "Satisfies: success with an int", checker: qt.Satisfies, got: 42, args: []interface{}{ func(v int) bool { return v == 42 }, }, expectedNegateFailure: ` error: unexpected success arg: int(42) predicate function: func(int) bool {...} `, }, { about: "Satisfies: success with nil", checker: qt.Satisfies, got: nil, args: []interface{}{ func(v []int) bool { return true }, }, expectedNegateFailure: ` error: unexpected success arg: nil predicate function: func([]int) bool {...} `, }, { about: "Satisfies: failure with an error", checker: qt.Satisfies, got: nil, args: []interface{}{qt.IsBadCheck}, expectedCheckFailure: ` error: value does not satisfy predicate function arg: nil predicate function: func(error) bool {...} `, }, { about: "Satisfies: failure with a string", checker: qt.Satisfies, got: "bad wolf", args: []interface{}{ func(string) bool { return false }, }, expectedCheckFailure: ` error: value does not satisfy predicate function arg: "bad wolf" predicate function: func(string) bool {...} `, }, { about: "Satisfies: not a function", checker: qt.Satisfies, got: 42, args: []interface{}{42}, expectedCheckFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: int(42) `, expectedNegateFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: int(42) `, }, { about: "Satisfies: function accepting no arguments", checker: qt.Satisfies, got: 42, args: []interface{}{ func() bool { return true }, }, expectedCheckFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func() bool {...} `, expectedNegateFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func() bool {...} `, }, { about: "Satisfies: function accepting too many arguments", checker: qt.Satisfies, got: 42, args: []interface{}{ func(int, string) bool { return false }, }, expectedCheckFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(int, string) bool {...} `, expectedNegateFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(int, string) bool {...} `, }, { about: "Satisfies: function returning no arguments", checker: qt.Satisfies, got: 42, args: []interface{}{ func(error) {}, }, expectedCheckFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(error) {...} `, expectedNegateFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(error) {...} `, }, { about: "Satisfies: function returning too many argments", checker: qt.Satisfies, got: 42, args: []interface{}{ func(int) (bool, error) { return true, nil }, }, expectedCheckFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(int) (bool, error) {...} `, expectedNegateFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(int) (bool, error) {...} `, }, { about: "Satisfies: function not returning a bool", checker: qt.Satisfies, got: 42, args: []interface{}{ func(int) error { return nil }, }, expectedCheckFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(int) error {...} `, expectedNegateFailure: ` error: bad check: predicate function is not a func(T) bool predicate function: func(int) error {...} `, }, { about: "Satisfies: type mismatch", checker: qt.Satisfies, got: 42, args: []interface{}{qt.IsBadCheck}, expectedCheckFailure: ` error: bad check: cannot use value of type int as type error in argument to predicate function arg: int(42) predicate function: func(error) bool {...} `, expectedNegateFailure: ` error: bad check: cannot use value of type int as type error in argument to predicate function arg: int(42) predicate function: func(error) bool {...} `, }, { about: "Satisfies: nil value that cannot be nil", checker: qt.Satisfies, got: nil, args: []interface{}{ func(string) bool { return true }, }, expectedCheckFailure: ` error: bad check: cannot use nil as type string in argument to predicate function predicate function: func(string) bool {...} `, expectedNegateFailure: ` error: bad check: cannot use nil as type string in argument to predicate function predicate function: func(string) bool {...} `, }, { about: "Satisfies: not enough arguments", checker: qt.Satisfies, expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: predicate function `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: predicate function `, }, { about: "Satisfies: too many arguments", checker: qt.Satisfies, got: 42, args: []interface{}{func() bool { return true }, 1, 2}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 3, want 1 got args: []interface {}{ func() bool {...}, int(1), int(2), } want args: predicate function `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 3, want 1 got args: []interface {}{ func() bool {...}, int(1), int(2), } want args: predicate function `, }, { about: "IsTrue: success", checker: qt.IsTrue, got: true, expectedNegateFailure: ` error: unexpected success got: bool(true) `, }, { about: "IsTrue: failure", checker: qt.IsTrue, got: false, expectedCheckFailure: ` error: value is not true got: bool(false) `, }, { about: "IsTrue: success with subtype", checker: qt.IsTrue, got: boolean(true), expectedNegateFailure: ` error: unexpected success got: quicktest_test.boolean(true) `, }, { about: "IsTrue: failure with subtype", checker: qt.IsTrue, got: boolean(false), expectedCheckFailure: ` error: value is not true got: quicktest_test.boolean(false) `, }, { about: "IsTrue: nil value", checker: qt.IsTrue, got: nil, expectedCheckFailure: ` error: bad check: value does not have a bool underlying type value: nil `, expectedNegateFailure: ` error: bad check: value does not have a bool underlying type value: nil `, }, { about: "IsTrue: non-bool value", checker: qt.IsTrue, got: 42, expectedCheckFailure: ` error: bad check: value does not have a bool underlying type value: int(42) `, expectedNegateFailure: ` error: bad check: value does not have a bool underlying type value: int(42) `, }, { about: "IsFalse: success", checker: qt.IsFalse, got: false, expectedNegateFailure: ` error: unexpected success got: bool(false) `, }, { about: "IsFalse: failure", checker: qt.IsFalse, got: true, expectedCheckFailure: ` error: value is not false got: bool(true) `, }, { about: "IsFalse: success with subtype", checker: qt.IsFalse, got: boolean(false), expectedNegateFailure: ` error: unexpected success got: quicktest_test.boolean(false) `, }, { about: "IsFalse: failure with subtype", checker: qt.IsFalse, got: boolean(true), expectedCheckFailure: ` error: value is not false got: quicktest_test.boolean(true) `, }, { about: "IsFalse: nil value", checker: qt.IsFalse, got: nil, expectedCheckFailure: ` error: bad check: value does not have a bool underlying type value: nil `, expectedNegateFailure: ` error: bad check: value does not have a bool underlying type value: nil `, }, { about: "IsFalse: non-bool value", checker: qt.IsFalse, got: "bad wolf", expectedCheckFailure: ` error: bad check: value does not have a bool underlying type value: "bad wolf" `, expectedNegateFailure: ` error: bad check: value does not have a bool underlying type value: "bad wolf" `, }, { about: "Not: success", checker: qt.Not(qt.IsNil), got: 42, expectedNegateFailure: ` error: got non-nil value got: int(42) `, }, { about: "Not: failure", checker: qt.Not(qt.IsNil), got: nil, expectedCheckFailure: ` error: unexpected success got: nil `, }, { about: "Not: not enough arguments", checker: qt.Not(qt.PanicMatches), expectedCheckFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, expectedNegateFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: regexp `, }, { about: "Not: too many arguments", checker: qt.Not(qt.Equals), args: []interface{}{42, nil}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ int(42), nil, } want args: want `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ int(42), nil, } want args: want `, }, { about: "Contains with string", checker: qt.Contains, got: "hello, world", args: []interface{}{"world"}, expectedNegateFailure: ` error: unexpected success container: "hello, world" want: "world" `, }, { about: "Contains with string no match", checker: qt.Contains, got: "hello, world", args: []interface{}{"worlds"}, expectedCheckFailure: ` error: no substring match found container: "hello, world" want: "worlds" `, }, { about: "Contains with slice", checker: qt.Contains, got: []string{"a", "b", "c"}, args: []interface{}{"a"}, expectedNegateFailure: ` error: unexpected success container: []string{"a", "b", "c"} want: "a" `, }, { about: "Contains with map", checker: qt.Contains, // Note: we can't use more than one element here because // pretty.Print output is non-deterministic. // https://github.com/kr/pretty/issues/47 got: map[string]string{"a": "d"}, args: []interface{}{"d"}, expectedNegateFailure: ` error: unexpected success container: map[string]string{"a":"d"} want: "d" `, }, { about: "Contains with non-string", checker: qt.Contains, got: "aa", args: []interface{}{5}, expectedCheckFailure: ` error: bad check: strings can only contain strings, not int `, expectedNegateFailure: ` error: bad check: strings can only contain strings, not int `, }, { about: "All slice equals", checker: qt.All(qt.Equals), got: []string{"a", "a"}, args: []interface{}{"a"}, expectedNegateFailure: ` error: unexpected success container: []string{"a", "a"} want: "a" `, }, { about: "All slice match", checker: qt.All(qt.Matches), got: []string{"red", "blue", "green"}, args: []interface{}{".*e.*"}, expectedNegateFailure: ` error: unexpected success container: []string{"red", "blue", "green"} regexp: ".*e.*" `, }, { about: "All nested match", checker: qt.All(qt.All(qt.Matches)), got: [][]string{{"hello", "goodbye"}, {"red", "blue"}, {}}, args: []interface{}{".*e.*"}, expectedNegateFailure: ` error: unexpected success container: [][]string{ {"hello", "goodbye"}, {"red", "blue"}, {}, } regexp: ".*e.*" `, }, { about: "All nested mismatch", checker: qt.All(qt.All(qt.Matches)), got: [][]string{{"hello", "goodbye"}, {"black", "blue"}, {}}, args: []interface{}{".*e.*"}, expectedCheckFailure: ` error: mismatch at index 1 error: mismatch at index 0 error: value does not match regexp first mismatched element: "black" `, }, { about: "All slice mismatch", checker: qt.All(qt.Matches), got: []string{"red", "black"}, args: []interface{}{".*e.*"}, expectedCheckFailure: ` error: mismatch at index 1 error: value does not match regexp first mismatched element: "black" `, }, { about: "All slice mismatch with DeepEqual", checker: qt.All(qt.DeepEquals), got: [][]string{{"a", "b"}, {"a", "c"}}, args: []interface{}{[]string{"a", "b"}}, expectedCheckFailure: ` error: mismatch at index 1 error: values are not deep equal diff (-got +want): ` + diff([]string{"a", "c"}, []string{"a", "b"}) + ` `, }, { about: "All bad checker args count", checker: qt.All(qt.IsNil), got: []int{}, args: []interface{}{5}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 got args: []interface {}{ int(5), } `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 got args: []interface {}{ int(5), } `, }, { about: "All bad checker args", checker: qt.All(qt.Matches), got: []string{"hello"}, args: []interface{}{5}, expectedCheckFailure: ` error: bad check: at index 0: bad check: regexp is not a string `, expectedNegateFailure: ` error: bad check: at index 0: bad check: regexp is not a string `, }, { about: "All with non-container", checker: qt.All(qt.Equals), got: 5, args: []interface{}{5}, expectedCheckFailure: ` error: bad check: map, slice or array required `, expectedNegateFailure: ` error: bad check: map, slice or array required `, }, { about: "All mismatch with map", checker: qt.All(qt.Matches), got: map[string]string{"a": "red", "b": "black"}, args: []interface{}{".*e.*"}, expectedCheckFailure: ` error: mismatch at key "b" error: value does not match regexp first mismatched element: "black" `, }, { about: "Any with non-container", checker: qt.Any(qt.Equals), got: 5, args: []interface{}{5}, expectedCheckFailure: ` error: bad check: map, slice or array required `, expectedNegateFailure: ` error: bad check: map, slice or array required `, }, { about: "Any no match", checker: qt.Any(qt.Equals), got: []int{}, args: []interface{}{5}, expectedCheckFailure: ` error: no matching element found container: []int{} want: int(5) `, }, { about: "Any bad checker arg count", checker: qt.Any(qt.IsNil), got: []int{}, args: []interface{}{5}, expectedCheckFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 got args: []interface {}{ int(5), } `, expectedNegateFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 got args: []interface {}{ int(5), } `, }, { about: "Any bad checker args", checker: qt.Any(qt.Matches), got: []string{"hello"}, args: []interface{}{5}, expectedCheckFailure: ` error: bad check: at index 0: bad check: regexp is not a string `, expectedNegateFailure: ` error: bad check: at index 0: bad check: regexp is not a string `, }, { about: "JSONEquals simple", checker: qt.JSONEquals, got: `{"First": 47.11}`, args: []interface{}{ &OuterJSON{ First: 47.11, }, }, expectedNegateFailure: tilde2bq(` error: unexpected success got: ~{"First": 47.11}~ want: &quicktest_test.OuterJSON{ First: 47.11, Second: nil, } `), }, { about: "JSONEquals nested", checker: qt.JSONEquals, got: `{"First": 47.11, "Last": [{"First": "Hello", "Second": 42}]}`, args: []interface{}{ &OuterJSON{ First: 47.11, Second: []*InnerJSON{ {First: "Hello", Second: 42}, }, }, }, expectedNegateFailure: tilde2bq(` error: unexpected success got: ~{"First": 47.11, "Last": [{"First": "Hello", "Second": 42}]}~ want: &quicktest_test.OuterJSON{ First: 47.11, Second: { &quicktest_test.InnerJSON{ First: "Hello", Second: 42, Third: {}, }, }, } `), }, { about: "JSONEquals nested with newline", checker: qt.JSONEquals, got: `{"First": 47.11, "Last": [{"First": "Hello", "Second": 42}, {"First": "World", "Third": {"F": false}}]}`, args: []interface{}{ &OuterJSON{ First: 47.11, Second: []*InnerJSON{ {First: "Hello", Second: 42}, {First: "World", Third: map[string]bool{ "F": false, }}, }, }, }, expectedNegateFailure: ` error: unexpected success got: "{\"First\": 47.11, \"Last\": [{\"First\": \"Hello\", \"Second\": 42},\n\t\t\t{\"First\": \"World\", \"Third\": {\"F\": false}}]}" want: &quicktest_test.OuterJSON{ First: 47.11, Second: { &quicktest_test.InnerJSON{ First: "Hello", Second: 42, Third: {}, }, &quicktest_test.InnerJSON{ First: "World", Second: 0, Third: {"F":false}, }, }, } `, }, { about: "JSONEquals extra field", checker: qt.JSONEquals, got: `{"NotThere": 1}`, args: []interface{}{ &OuterJSON{ First: 2, }, }, expectedCheckFailure: fmt.Sprintf(` error: values are not deep equal diff (-got +want): %s `, diff(map[string]interface{}{"NotThere": 1.0}, map[string]interface{}{"First": 2.0})), }, { about: "JSONEquals cannot unmarshal obtained value", checker: qt.JSONEquals, got: `{"NotThere": `, args: []interface{}{nil}, expectedCheckFailure: fmt.Sprintf(tilde2bq(` error: cannot unmarshal obtained contents: %s; "{\"NotThere\": " got: ~{"NotThere": ~ want: nil `), mustJSONUnmarshalErr(`{"NotThere": `)), }, { about: "JSONEquals cannot marshal expected value", checker: qt.JSONEquals, got: `null`, args: []interface{}{ jsonErrorMarshaler{}, }, expectedCheckFailure: ` error: bad check: cannot marshal expected contents: json: error calling MarshalJSON for type quicktest_test.jsonErrorMarshaler: qt json marshal error `, expectedNegateFailure: ` error: bad check: cannot marshal expected contents: json: error calling MarshalJSON for type quicktest_test.jsonErrorMarshaler: qt json marshal error `, }, { about: "JSONEquals with []byte", checker: qt.JSONEquals, got: []byte("null"), args: []interface{}{nil}, expectedNegateFailure: ` error: unexpected success got: []uint8("null") want: nil `, }, { about: "JSONEquals with RawMessage", checker: qt.JSONEquals, got: []byte("null"), args: []interface{}{json.RawMessage("null")}, expectedNegateFailure: ` error: unexpected success got: []uint8("null") want: json.RawMessage("null") `, }, { about: "JSONEquals with bad type", checker: qt.JSONEquals, got: 0, args: []interface{}{nil}, expectedCheckFailure: ` error: bad check: expected string or byte, got int `, expectedNegateFailure: ` error: bad check: expected string or byte, got int `, }, { about: "CodecEquals with bad marshal", checker: qt.CodecEquals( func(x interface{}) ([]byte, error) { return []byte("bad json"), nil }, json.Unmarshal, ), got: "null", args: []interface{}{nil}, expectedCheckFailure: fmt.Sprintf(` error: bad check: cannot unmarshal expected contents: %s `, mustJSONUnmarshalErr("bad json")), expectedNegateFailure: fmt.Sprintf(` error: bad check: cannot unmarshal expected contents: %s `, mustJSONUnmarshalErr("bad json")), }, { about: "CodecEquals with options", checker: qt.CodecEquals( json.Marshal, json.Unmarshal, cmpopts.SortSlices(func(x, y interface{}) bool { return x.(string) < y.(string) }), ), got: `["b", "z", "c", "a"]`, args: []interface{}{[]string{"a", "c", "z", "b"}}, expectedNegateFailure: tilde2bq(` error: unexpected success got: ~["b", "z", "c", "a"]~ want: []string{"a", "c", "z", "b"} `), }} func TestCheckers(t *testing.T) { for _, test := range checkerTests { checker := qt.WithVerbosity(test.checker, test.verbose) t.Run(test.about, func(t *testing.T) { tt := &testingT{} c := qt.New(tt) ok := c.Check(test.got, checker, test.args...) checkResult(t, ok, tt.errorString(), test.expectedCheckFailure) }) t.Run("Not "+test.about, func(t *testing.T) { tt := &testingT{} c := qt.New(tt) ok := c.Check(test.got, qt.Not(checker), test.args...) checkResult(t, ok, tt.errorString(), test.expectedNegateFailure) }) } } func diff(x, y interface{}, opts ...cmp.Option) string { d := cmp.Diff(x, y, opts...) return strings.TrimSuffix(qt.Prefixf(" ", "%s", d), "\n") } type jsonErrorMarshaler struct{} func (jsonErrorMarshaler) MarshalJSON() ([]byte, error) { return nil, fmt.Errorf("qt json marshal error") } func mustJSONUnmarshalErr(s string) error { var v interface{} err := json.Unmarshal([]byte(s), &v) if err == nil { panic("want JSON error, got nil") } return err } func tilde2bq(s string) string { return strings.Replace(s, "~", "`", -1) } quicktest-1.14.0/cleanup_test.go000066400000000000000000000024351413325465400166410ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build go1.14 // +build go1.14 package quicktest_test import ( "testing" qt "github.com/frankban/quicktest" ) // This file defines tests that are only valid since the Cleanup // method was added in Go 1.14. func TestCCleanup(t *testing.T) { c := qt.New(t) cleanups := 0 c.Run("defer", func(c *qt.C) { c.Cleanup(func() { cleanups++ }) }) c.Assert(cleanups, qt.Equals, 1) } func TestCDeferWithoutDone(t *testing.T) { c := qt.New(t) tc := &testingTWithCleanup{ TB: t, cleanup: func() {}, } c1 := qt.New(tc) c1.Defer(func() {}) c1.Defer(func() {}) c.Assert(tc.cleanup, qt.PanicMatches, `Done not called after Defer`) } func TestCDeferVsCleanupOrder(t *testing.T) { c := qt.New(t) var defers []int c.Run("subtest", func(c *qt.C) { c.Defer(func() { defers = append(defers, 0) }) c.Cleanup(func() { defers = append(defers, 1) }) c.Defer(func() { defers = append(defers, 2) }) c.Cleanup(func() { defers = append(defers, 3) }) }) c.Assert(defers, qt.DeepEquals, []int{3, 2, 1, 0}) } type testingTWithCleanup struct { testing.TB cleanup func() } func (t *testingTWithCleanup) Cleanup(f func()) { oldCleanup := t.cleanup t.cleanup = func() { defer oldCleanup() f() } } quicktest-1.14.0/comment.go000066400000000000000000000016141413325465400156130ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import "fmt" // Commentf returns a test comment whose output is formatted according to // the given format specifier and args. It may be provided as the last argument // to any check or assertion and will be displayed if the check or assertion // fails. For instance: // // c.Assert(a, qt.Equals, 42, qt.Commentf("answer is not %d", 42)) // func Commentf(format string, args ...interface{}) Comment { return Comment{ format: format, args: args, } } // Comment represents additional information on a check or an assertion which is // displayed when the check or assertion fails. type Comment struct { format string args []interface{} } // String outputs a string formatted according to the stored format specifier // and args. func (c Comment) String() string { return fmt.Sprintf(c.format, c.args...) } quicktest-1.14.0/comment_test.go000066400000000000000000000012201413325465400166430ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "testing" qt "github.com/frankban/quicktest" ) func TestCommentf(t *testing.T) { c := qt.Commentf("the answer is %d", 42) comment := c.String() expectedComment := "the answer is 42" if comment != expectedComment { t.Fatalf("comment error:\ngot %q\nwant %q", comment, expectedComment) } } func TestConstantCommentf(t *testing.T) { const expectedComment = "bad wolf" c := qt.Commentf(expectedComment) comment := c.String() if comment != expectedComment { t.Fatalf("constant comment error:\ngot %q\nwant %q", comment, expectedComment) } } quicktest-1.14.0/deferpanic_test.go000066400000000000000000000015371413325465400173140ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build !go1.14 // +build !go1.14 package quicktest_test import ( "testing" qt "github.com/frankban/quicktest" ) func TestCDeferCalledEvenAfterDeferPanic(t *testing.T) { // This test doesn't test anything useful under go 1.14 and // later when Cleanup is built in. c := qt.New(t) deferred1 := 0 deferred2 := 0 c.Defer(func() { deferred1++ }) c.Defer(func() { panic("scream and shout") }) c.Defer(func() { deferred2++ }) c.Defer(func() { panic("run in circles") }) func() { defer func() { c.Check(recover(), qt.Equals, "scream and shout") }() c.Done() }() c.Assert(deferred1, qt.Equals, 1) c.Assert(deferred2, qt.Equals, 1) // Check that calling Done again doesn't panic. c.Done() c.Assert(deferred1, qt.Equals, 1) c.Assert(deferred2, qt.Equals, 1) } quicktest-1.14.0/doc.go000066400000000000000000000225031413325465400147160ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. /* Package quicktest provides a collection of Go helpers for writing tests. Quicktest helpers can be easily integrated inside regular Go tests, for instance: import qt "github.com/frankban/quicktest" func TestFoo(t *testing.T) { t.Run("numbers", func(t *testing.T) { c := qt.New(t) numbers, err := somepackage.Numbers() c.Assert(numbers, qt.DeepEquals, []int{42, 47}) c.Assert(err, qt.ErrorMatches, "bad wolf") }) t.Run("nil", func(t *testing.T) { c := qt.New(t) got := somepackage.MaybeNil() c.Assert(got, qt.IsNil, qt.Commentf("value: %v", somepackage.Value)) }) } Assertions An assertion looks like this, where qt.Equals could be replaced by any available checker. If the assertion fails, the underlying Fatal method is called to describe the error and abort the test. c := qt.New(t) c.Assert(someValue, qt.Equals, wantValue) If you don’t want to abort on failure, use Check instead, which calls Error instead of Fatal: c.Check(someValue, qt.Equals, wantValue) For really short tests, the extra line for instantiating *qt.C can be avoided: qt.Assert(t, someValue, qt.Equals, wantValue) qt.Check(t, someValue, qt.Equals, wantValue) The library provides some base checkers like Equals, DeepEquals, Matches, ErrorMatches, IsNil and others. More can be added by implementing the Checker interface. Below, we list the checkers implemented by the package in alphabetical order. All All returns a Checker that uses the given checker to check elements of slice or array or the values of a map. It succeeds if all elements pass the check. On failure it prints the error from the first index that failed. For example: c.Assert([]int{3, 5, 8}, qt.All(qt.Not(qt.Equals)), 0) c.Assert([][]string{{"a", "b"}, {"a", "b"}}, qt.All(qt.DeepEquals), []string{"c", "d"}) See also Any and Contains. Any Any returns a Checker that uses the given checker to check elements of a slice or array or the values from a map. It succeeds if any element passes the check. For example: c.Assert([]int{3,5,7,99}, qt.Any(qt.Equals), 7) c.Assert([][]string{{"a", "b"}, {"c", "d"}}, qt.Any(qt.DeepEquals), []string{"c", "d"}) See also All and Contains. CmpEquals CmpEquals checks equality of two arbitrary values according to the provided compare options. DeepEquals is more commonly used when no compare options are required. Example calls: c.Assert(list, qt.CmpEquals(cmpopts.SortSlices), []int{42, 47}) c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals. CodecEquals CodecEquals returns a checker that checks for codec value equivalence. func CodecEquals( marshal func(interface{}) ([]byte, error), unmarshal func([]byte, interface{}) error, opts ...cmp.Option, ) Checker It expects two arguments: a byte slice or a string containing some codec-marshaled data, and a Go value. It uses unmarshal to unmarshal the data into an interface{} value. It marshals the Go value using marshal, then unmarshals the result into an interface{} value. It then checks that the two interface{} values are deep-equal to one another, using CmpEquals(opts) to perform the check. See JSONEquals for an example of this in use. Contains Contains checks that a map, slice, array or string contains a value. It's the same as using Any(Equals), except that it has a special case for strings - if the first argument is a string, the second argument must also be a string and strings.Contains will be used. For example: c.Assert("hello world", qt.Contains, "world") c.Assert([]int{3,5,7,99}, qt.Contains, 7) ContentEquals ContentEquals is is like DeepEquals but any slices in the compared values will be sorted before being compared. For example: c.Assert([]string{"c", "a", "b"}, qt.ContentEquals, []string{"a", "b", "c"}) DeepEquals DeepEquals checks that two arbitrary values are deeply equal. The comparison is done using the github.com/google/go-cmp/cmp package. When comparing structs, by default no exported fields are allowed. If a more sophisticated comparison is required, use CmpEquals (see below). Example call: c.Assert(got, qt.DeepEquals, []int{42, 47}) Equals Equals checks that two values are equal, as compared with Go's == operator. For instance: c.Assert(answer, qt.Equals, 42) Note that the following will fail: c.Assert((*sometype)(nil), qt.Equals, nil) Use the IsNil checker below for this kind of nil check. ErrorAs ErrorAs checks that the error is or wraps a specific error type. If so, it assigns it to the provided pointer. This is analogous to calling errors.As. For instance: // Checking for a specific error type c.Assert(err, qt.ErrorAs, new(*os.PathError)) // Checking fields on a specific error type var pathError *os.PathError if c.Check(err, qt.ErrorAs, &pathError) { c.Assert(pathError.Path, Equals, "some_path") } ErrorIs ErrorIs checks that the error is or wraps a specific error value. This is analogous to calling errors.Is. For instance: c.Assert(err, qt.ErrorIs, os.ErrNotExist) ErrorMatches ErrorMatches checks that the provided value is an error whose message matches the provided regular expression. For instance: c.Assert(err, qt.ErrorMatches, `bad wolf .*`) HasLen HasLen checks that the provided value has the given length. For instance: c.Assert([]int{42, 47}, qt.HasLen, 2) c.Assert(myMap, qt.HasLen, 42) Implements Implements checks that the provided value implements an interface. The interface is specified with a pointer to an interface variable. For instance: var rc io.ReadCloser c.Assert(myReader, qt.Implements, &rc) IsFalse IsFalse checks that the provided value is false. The value must have a boolean underlying type. For instance: c.Assert(false, qt.IsFalse) c.Assert(IsValid(), qt.IsFalse) IsNil IsNil checks that the provided value is nil. For instance: c.Assert(got, qt.IsNil) As a special case, if the value is nil but implements the error interface, it is still considered to be non-nil. This means that IsNil will fail on an error value that happens to have an underlying nil value, because that's invariably a mistake. See https://golang.org/doc/faq#nil_error. So it's just fine to check an error like this: c.Assert(err, qt.IsNil) IsNotNil IsNotNil is a Checker checking that the provided value is not nil. IsNotNil is the equivalent of qt.Not(qt.IsNil) For instance: c.Assert(got, qt.IsNotNil) IsTrue IsTrue checks that the provided value is true. The value must have a boolean underlying type. For instance: c.Assert(true, qt.IsTrue) c.Assert(myBoolean(false), qt.IsTrue) JSONEquals JSONEquals checks whether a byte slice or string is JSON-equivalent to a Go value. See CodecEquals for more information. It uses DeepEquals to do the comparison. If a more sophisticated comparison is required, use CodecEquals directly. For instance: c.Assert(`{"First": 47.11}`, qt.JSONEquals, &MyStruct{First: 47.11}) Matches Matches checks that a string or result of calling the String method (if the value implements fmt.Stringer) matches the provided regular expression. For instance: c.Assert("these are the voyages", qt.Matches, `these are .*`) c.Assert(net.ParseIP("1.2.3.4"), qt.Matches, `1.*`) Not Not returns a Checker negating the given Checker. For instance: c.Assert(got, qt.Not(qt.IsNil)) c.Assert(answer, qt.Not(qt.Equals), 42) PanicMatches PanicMatches checks that the provided function panics with a message matching the provided regular expression. For instance: c.Assert(func() {panic("bad wolf ...")}, qt.PanicMatches, `bad wolf .*`) Satisfies Satisfies checks that the provided value, when used as argument of the provided predicate function, causes the function to return true. The function must be of type func(T) bool, having got assignable to T. For instance: // Check that an error from os.Open satisfies os.IsNotExist. c.Assert(err, qt.Satisfies, os.IsNotExist) // Check that a floating point number is a not-a-number. c.Assert(f, qt.Satisfies, math.IsNaN) Deferred Execution The testing.TB.Cleanup helper provides the ability to defer the execution of functions that will be run when the test completes. This is often useful for creating OS-level resources such as temporary directories (see c.Mkdir). When targeting Go versions that don't have Cleanup (< 1.14), the same can be achieved using c.Defer. In this case, to trigger the deferred behavior, calling c.Done is required. For instance, if you create a *C instance at the top level, you’ll have to add a defer to trigger the cleanups at the end of the test: defer c.Done() However, if you use quicktest to create a subtest, Done will be called automatically at the end of that subtest. For example: func TestFoo(t *testing.T) { c := qt.New(t) c.Run("subtest", func(c *qt.C) { c.Setenv("HOME", c.Mkdir()) // Here $HOME is set the path to a newly created directory. // At the end of the test the directory will be removed // and HOME set back to its original value. }) } The c.Patch, c.Setenv, c.Unsetenv and c.Mkdir helpers use t.Cleanup for cleaning up resources when available, and fall back to Defer otherwise. */ package quicktest quicktest-1.14.0/error.go000066400000000000000000000020711413325465400153000ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "fmt" ) // BadCheckf returns an error used to report a problem with the checker // invocation or testing execution itself (like wrong number or type of // arguments) rather than a real Check or Assert failure. // This helper can be used when implementing checkers. func BadCheckf(format string, a ...interface{}) error { e := badCheck(fmt.Sprintf(format, a...)) return &e } // IsBadCheck reports whether the given error has been created by BadCheckf. // This helper can be used when implementing checkers. func IsBadCheck(err error) bool { _, ok := err.(*badCheck) return ok } type badCheck string // Error implements the error interface. func (e *badCheck) Error() string { return "bad check: " + string(*e) } // ErrSilent is the error used when there is no need to include in the failure // output the "error" and "check" keys and all the keys automatically // added for args. This helper can be used when implementing checkers. var ErrSilent = fmt.Errorf("silent failure") quicktest-1.14.0/error_test.go000066400000000000000000000021071413325465400163370ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "errors" "fmt" "testing" qt "github.com/frankban/quicktest" ) func TestBadCheckf(t *testing.T) { err := qt.BadCheckf("bad %s", "wolf") expectedMessage := "bad check: bad wolf" if err.Error() != expectedMessage { t.Fatalf("error:\ngot %q\nwant %q", err, expectedMessage) } } func TestIsBadCheck(t *testing.T) { err := qt.BadCheckf("bad wolf") assertBool(t, qt.IsBadCheck(err), true) err = errors.New("bad wolf") assertBool(t, qt.IsBadCheck(err), false) } var errBadWolf = &errTest{ msg: "bad wolf", formatted: true, } // errTest is an error type used in tests. type errTest struct { msg string formatted bool } // Error implements error. func (err *errTest) Error() string { return err.msg } // Format implements fmt.Formatter. func (err *errTest) Format(f fmt.State, c rune) { if !f.Flag('+') || c != 'v' { fmt.Fprint(f, "unexpected verb for formatting the error") } fmt.Fprint(f, err.Error()) if err.formatted { fmt.Fprint(f, "\n file:line") } } quicktest-1.14.0/export_test.go000066400000000000000000000013351413325465400165310ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest var Prefixf = prefixf // WithVerbosity returns the given checker with a verbosity level of v. // A copy of the original checker is made if mutating is required. func WithVerbosity(c Checker, v bool) Checker { switch checker := c.(type) { case *allChecker: c := *checker c.elemChecker = WithVerbosity(c.elemChecker, v) return &c case *anyChecker: c := *checker c.elemChecker = WithVerbosity(c.elemChecker, v) return &c case *cmpEqualsChecker: c := *checker c.verbose = func() bool { return v } return &c case *codecEqualChecker: c := *checker c.deepEquals = WithVerbosity(c.deepEquals, v) return &c } return c } quicktest-1.14.0/format.go000066400000000000000000000046051413325465400154440ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "fmt" "reflect" "strconv" "strings" "unicode/utf8" "github.com/kr/pretty" ) // Format formats the given value as a string. It is used to print values in // test failures unless that's changed by calling C.SetFormat. func Format(v interface{}) string { switch v := v.(type) { case error: s, ok := checkStringCall(v, v.Error) if !ok { return "e" } if msg := fmt.Sprintf("%+v", v); msg != s { // The error has formatted itself with additional information. // Leave that as is. return msg } return "e" + quoteString(s) case fmt.Stringer: s, ok := checkStringCall(v, v.String) if !ok { return "s" } return "s" + quoteString(s) case string: return quoteString(v) case uintptr, uint, uint8, uint16, uint32, uint64: // Use decimal base (rather than hexadecimal) for representing uint types. return fmt.Sprintf("%T(%d)", v, v) } if bytes, ok := byteSlice(v); ok && bytes != nil && utf8.Valid(bytes) { // It's a top level slice of bytes that's also valid UTF-8. // Ideally, this would happen at deeper levels too, // but this is sufficient for some significant cases // (json.RawMessage for example). return fmt.Sprintf("%T(%s)", v, quoteString(string(bytes))) } // The pretty.Sprint equivalent does not quote string values. return fmt.Sprintf("%# v", pretty.Formatter(v)) } func byteSlice(x interface{}) ([]byte, bool) { v := reflect.ValueOf(x) if !v.IsValid() { return nil, false } t := v.Type() if t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { return v.Bytes(), true } return nil, false } func quoteString(s string) string { // TODO think more about what to do about multi-line strings. if strings.Contains(s, `"`) && !strings.Contains(s, "\n") && strconv.CanBackquote(s) { return "`" + s + "`" } return strconv.Quote(s) } // checkStringCall calls f and returns its result, and reports if the call // succeeded without panicking due to a nil pointer. // If f panics and v is a nil pointer, it returns false. func checkStringCall(v interface{}, f func() string) (s string, ok bool) { defer func() { err := recover() if err == nil { return } if val := reflect.ValueOf(v); val.Kind() == reflect.Ptr && val.IsNil() { ok = false return } panic(err) }() return f(), true } type formatFunc func(interface{}) string quicktest-1.14.0/format_test.go000066400000000000000000000056521413325465400165060ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "bytes" "testing" qt "github.com/frankban/quicktest" ) var formatTests = []struct { about string value interface{} want string }{{ about: "error value", value: errBadWolf, want: "bad wolf\n file:line", }, { about: "error value: not formatted", value: &errTest{ msg: "exterminate!", }, want: `e"exterminate!"`, }, { about: "error value: with quotes", value: &errTest{ msg: `cannot open "/no/such/file"`, }, want: "e`cannot open \"/no/such/file\"`", }, { about: "error value: multi-line", value: &errTest{ msg: `err: "these are the voyages"`, }, want: `e"err:\n\"these are the voyages\""`, }, { about: "error value: with backquotes", value: &errTest{ msg: "cannot `open` \"file\"", }, want: `e"cannot ` + "`open`" + ` \"file\""`, }, { about: "error value: not guarding against nil", value: (*errTest)(nil), want: `e`, }, { about: "stringer", value: bytes.NewBufferString("I am a stringer"), want: `s"I am a stringer"`, }, { about: "stringer: with quotes", value: bytes.NewBufferString(`I say "hello"`), want: "s`I say \"hello\"`", }, { about: "stringer: not guarding against nil", value: (*nilStringer)(nil), want: "s", }, { about: "string", value: "these are the voyages", want: `"these are the voyages"`, }, { about: "string: with quotes", value: `here is a quote: "`, want: "`here is a quote: \"`", }, { about: "string: multi-line", value: `foo "bar" `, want: `"foo\n\"bar\"\n"`, }, { about: "string: with backquotes", value: `"` + "`", want: `"\"` + "`\"", }, { about: "slice", value: []int{1, 2, 3}, want: "[]int{1, 2, 3}", }, { about: "bytes", value: []byte("hello"), want: `[]uint8("hello")`, }, { about: "custom bytes type", value: myBytes("hello"), want: `quicktest_test.myBytes("hello")`, }, { about: "bytes with backquote", value: []byte(`a "b" c`), want: "[]uint8(`a \"b\" c`)", }, { about: "bytes with invalid utf-8", value: []byte("\xff"), want: "[]uint8{0xff}", }, { about: "nil byte slice", value: []byte(nil), want: "[]uint8(nil)", }, { about: "time", value: goTime, want: `s"2012-03-28 00:00:00 +0000 UTC"`, }, { about: "struct with byte slice", value: struct{ X []byte }{[]byte("x")}, want: "struct { X []uint8 }{\n X: {0x78},\n}", }, { about: "uint64", value: uint64(17), want: "uint64(17)", }, { about: "uint32", value: uint32(17898), want: "uint32(17898)", }, { about: "uintptr", value: uintptr(13), want: "uintptr(13)", }, } func TestFormat(t *testing.T) { for _, test := range formatTests { t.Run(test.about, func(t *testing.T) { got := qt.Format(test.value) if got != test.want { t.Fatalf("format:\ngot %q\nwant %q", got, test.want) } }) } } type myBytes []byte // nilStringer is a stringer not guarding against nil. type nilStringer struct { msg string } func (s *nilStringer) String() string { return s.msg } quicktest-1.14.0/go.mod000066400000000000000000000001711413325465400147250ustar00rootroot00000000000000module github.com/frankban/quicktest require ( github.com/google/go-cmp v0.5.6 github.com/kr/pretty v0.3.0 ) go 1.13 quicktest-1.14.0/go.sum000066400000000000000000000025611413325465400147570ustar00rootroot00000000000000github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= quicktest-1.14.0/iter.go000066400000000000000000000022161413325465400151130ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "fmt" "reflect" ) // containerIter provides an interface for iterating over a container // (map, slice or array). type containerIter interface { // next advances to the next item in the container. next() bool // key returns the current key as a string. key() string // value returns the current value. value() reflect.Value } // newIter returns an iterator over x which must be a map, slice // or array. func newIter(x interface{}) (containerIter, error) { v := reflect.ValueOf(x) switch v.Kind() { case reflect.Map: return newMapIter(v), nil case reflect.Slice, reflect.Array: return &sliceIter{ index: -1, v: v, }, nil default: return nil, fmt.Errorf("map, slice or array required") } } // sliceIter implements containerIter for slices and arrays. type sliceIter struct { v reflect.Value index int } func (i *sliceIter) next() bool { i.index++ return i.index < i.v.Len() } func (i *sliceIter) value() reflect.Value { return i.v.Index(i.index) } func (i *sliceIter) key() string { return fmt.Sprintf("index %d", i.index) } quicktest-1.14.0/mapiter.go000066400000000000000000000010211413325465400156020ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build go1.12 // +build go1.12 package quicktest import ( "fmt" "reflect" ) func newMapIter(v reflect.Value) containerIter { return mapIter{v.MapRange()} } // mapIter implements containerIter for maps. type mapIter struct { iter *reflect.MapIter } func (i mapIter) next() bool { return i.iter.Next() } func (i mapIter) key() string { return fmt.Sprintf("key %#v", i.iter.Key()) } func (i mapIter) value() reflect.Value { return i.iter.Value() } quicktest-1.14.0/mapiter_go1.11.go000066400000000000000000000016041413325465400165770ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build !go1.12 // +build !go1.12 package quicktest import ( "fmt" "reflect" ) func newMapIter(v reflect.Value) containerIter { return &mapIter{ v: v, keys: v.MapKeys(), index: -1, } } // mapIter implements containerIter for maps prior to the // introduction of reflect.Value.MapRange in Go 1.12. type mapIter struct { v reflect.Value keys []reflect.Value index int } func (i *mapIter) next() bool { i.index++ return i.index < len(i.keys) } func (i *mapIter) value() reflect.Value { v := i.v.MapIndex(i.keys[i.index]) if !v.IsValid() { // We've probably got a NaN key; we can't // get NaN keys from maps with reflect, // so just return the zero value. return reflect.Zero(i.v.Type().Elem()) } return v } func (i *mapIter) key() string { return fmt.Sprintf("key %#v", i.keys[i.index]) } quicktest-1.14.0/patch.go000066400000000000000000000036431413325465400152540ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "io/ioutil" "os" "reflect" ) // Patch sets a variable to a temporary value for the duration of the test. // // It sets the value pointed to by the given destination to the given // value, which must be assignable to the element type of the destination. // // At the end of the test (see "Deferred execution" in the package docs), the // destination is set back to its original value. func (c *C) Patch(dest, value interface{}) { destv := reflect.ValueOf(dest).Elem() oldv := reflect.New(destv.Type()).Elem() oldv.Set(destv) valuev := reflect.ValueOf(value) if !valuev.IsValid() { // This isn't quite right when the destination type is not // nilable, but it's better than the complex alternative. valuev = reflect.Zero(destv.Type()) } destv.Set(valuev) c.cleanup(func() { destv.Set(oldv) }) } // Unsetenv unsets an environment variable for the duration of a test. func (c *C) Unsetenv(name string) { c.Setenv(name, "") os.Unsetenv(name) } // Mkdir makes a temporary directory and returns its name. // // At the end of the test (see "Deferred execution" in the package docs), the // directory and its contents are removed. // // Deprecated: in Go >= 1.15 use testing.TB.TempDir instead. func (c *C) Mkdir() string { td, ok := c.TB.(interface { TempDir() string }) if ok { return td.TempDir() } name, err := ioutil.TempDir("", "quicktest-") c.Assert(err, Equals, nil) c.cleanup(func() { if err := os.RemoveAll(name); err != nil { // Don't call c.Check because the stack traverse logic won't // print the source location, so just log instead. c.Errorf("quicktest cannot remove temporary testing directory: %v", err) } }) return name } // cleanup uses Cleanup when it can, falling back to using Defer. func (c *C) cleanup(f func()) { if tb, ok := c.TB.(cleaner); ok { tb.Cleanup(f) } else { c.Defer(f) } } quicktest-1.14.0/patch_go1.14.go000066400000000000000000000024211413325465400162360ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build go1.14 // +build go1.14 package quicktest import "testing" // Patch sets a variable to a temporary value for the duration of the test. // // It sets the value pointed to by the given destination to the given value, // which must be assignable to the element type of the destination. // // At the end of the test the destination is set back to its original value // using t.Cleanup. // // The top level Patch function is only available on Go >= 1.14. Use (*C).Patch // when on prior versions. func Patch(t testing.TB, dest, value interface{}) { New(t).Patch(dest, value) } // Setenv sets an environment variable to a temporary value for the duration of // the test. // // At the end of the test the environment variable is returned to its original // value using t.Cleanup. // // The top level Setenv function is only available on Go >= 1.14. Use // (*C).Setenv when on prior versions. func Setenv(t testing.TB, name, val string) { New(t).Setenv(name, val) } // Unsetenv unsets an environment variable for the duration of a test. // // The top level Unsetenv function is only available on Go >= 1.14. Use // (*C).Unsetenv when on prior versions. func Unsetenv(t testing.TB, name string) { New(t).Unsetenv(name) } quicktest-1.14.0/patch_go1.14_test.go000066400000000000000000000016611413325465400173020ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build go1.14 // +build go1.14 package quicktest_test import ( "os" "testing" qt "github.com/frankban/quicktest" ) func TestPatchSetInt(t *testing.T) { i := 99 t.Run("subtest", func(t *testing.T) { qt.Patch(t, &i, 77) qt.Assert(t, i, qt.Equals, 77) }) qt.Assert(t, i, qt.Equals, 99) } func TestSetenv(t *testing.T) { const envName = "SOME_VAR" os.Setenv(envName, "initial") t.Run("subtest", func(t *testing.T) { qt.Setenv(t, envName, "a new value") qt.Check(t, os.Getenv(envName), qt.Equals, "a new value") }) qt.Check(t, os.Getenv(envName), qt.Equals, "initial") } func TestUnsetenv(t *testing.T) { const envName = "SOME_VAR" os.Setenv(envName, "initial") t.Run("subtest", func(t *testing.T) { qt.Unsetenv(t, envName) _, ok := os.LookupEnv(envName) qt.Assert(t, ok, qt.IsFalse) }) qt.Check(t, os.Getenv(envName), qt.Equals, "initial") } quicktest-1.14.0/patch_go1.17.go000066400000000000000000000012011413325465400162340ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build !go1.17 // +build !go1.17 package quicktest import "os" // Setenv sets an environment variable to a temporary value for the // duration of the test. // // At the end of the test (see "Deferred execution" in the package docs), the // environment variable is returned to its original value. // // This is the equivalent of testing.T.Setenv introduced in Go 1.17. func (c *C) Setenv(name, val string) { oldVal, oldOK := os.LookupEnv(name) os.Setenv(name, val) c.cleanup(func() { if oldOK { os.Setenv(name, oldVal) } else { os.Unsetenv(name) } }) } quicktest-1.14.0/patch_go1.17_test.go000066400000000000000000000013561413325465400173060ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. //go:build !go1.17 // +build !go1.17 package quicktest_test import ( "os" "testing" qt "github.com/frankban/quicktest" ) const envName = "SOME_VAR" func TestCSetenv(t *testing.T) { c := qt.New(t) os.Setenv(envName, "initial") testCleanup(t, func(c *qt.C) { c.Setenv(envName, "new value") c.Check(os.Getenv(envName), qt.Equals, "new value") }) c.Check(os.Getenv(envName), qt.Equals, "initial") } func TestCSetenvWithUnsetVariable(t *testing.T) { c := qt.New(t) os.Unsetenv(envName) testCleanup(t, func(c *qt.C) { c.Setenv(envName, "new value") c.Check(os.Getenv(envName), qt.Equals, "new value") }) _, ok := os.LookupEnv(envName) c.Assert(ok, qt.IsFalse) } quicktest-1.14.0/patch_test.go000066400000000000000000000050321413325465400163050ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "errors" "os" "path/filepath" "testing" qt "github.com/frankban/quicktest" ) func TestCPatchSetInt(t *testing.T) { c := qt.New(t) i := 99 testCleanup(t, func(c *qt.C) { c.Patch(&i, 88) c.Assert(i, qt.Equals, 88) }) c.Assert(i, qt.Equals, 99) } func TestCPatchSetError(t *testing.T) { c := qt.New(t) oldErr := errors.New("foo") newErr := errors.New("bar") err := oldErr testCleanup(t, func(c *qt.C) { c.Patch(&err, newErr) c.Assert(err, qt.Equals, newErr) }) c.Assert(err, qt.Equals, oldErr) } func TestCPatchSetErrorToNil(t *testing.T) { c := qt.New(t) oldErr := errors.New("foo") err := oldErr testCleanup(t, func(c *qt.C) { c.Patch(&err, nil) c.Assert(err, qt.IsNil) }) c.Assert(err, qt.Equals, oldErr) } func TestCPatchSetMapToNil(t *testing.T) { c := qt.New(t) oldMap := map[string]int{"foo": 1234} m := oldMap testCleanup(t, func(c *qt.C) { c.Patch(&m, nil) c.Assert(m, qt.IsNil) }) c.Assert(m, qt.DeepEquals, oldMap) } func TestCPatchPanicsWhenNotAssignable(t *testing.T) { c := qt.New(t) i := 99 type otherInt int c.Assert(func() { c.Patch(&i, otherInt(88)) }, qt.PanicMatches, `reflect\.Set: value of type quicktest_test\.otherInt is not assignable to type int`) } func TestCUnsetenv(t *testing.T) { c := qt.New(t) const envName = "SOME_VAR" os.Setenv(envName, "initial") testCleanup(t, func(c *qt.C) { c.Unsetenv(envName) _, ok := os.LookupEnv(envName) c.Assert(ok, qt.IsFalse) }) c.Check(os.Getenv(envName), qt.Equals, "initial") } func TestCUnsetenvWithUnsetVariable(t *testing.T) { c := qt.New(t) const envName = "SOME_VAR" os.Unsetenv(envName) testCleanup(t, func(c *qt.C) { c.Unsetenv(envName) _, ok := os.LookupEnv(envName) c.Assert(ok, qt.IsFalse) }) _, ok := os.LookupEnv(envName) c.Assert(ok, qt.IsFalse) } func TestCMkdir(t *testing.T) { c := qt.New(t) var dir string testCleanup(t, func(c *qt.C) { dir = c.Mkdir() c.Assert(dir, qt.Not(qt.Equals), "") info, err := os.Stat(dir) c.Assert(err, qt.IsNil) c.Assert(info.IsDir(), qt.IsTrue) f, err := os.Create(filepath.Join(dir, "hello")) c.Assert(err, qt.IsNil) f.Close() }) _, err := os.Stat(dir) c.Assert(err, qt.Not(qt.IsNil)) } func testCleanup(t *testing.T, f func(c *qt.C)) { t.Run("subtest", func(t *testing.T) { c := qt.New(t) if _, ok := c.TB.(cleaner); !ok { // Calling Done is required when testing on Go < 1.14. defer c.Done() } f(c) }) } type cleaner interface { Cleanup(func()) } quicktest-1.14.0/qtsuite/000077500000000000000000000000001413325465400153165ustar00rootroot00000000000000quicktest-1.14.0/qtsuite/suite.go000066400000000000000000000060661413325465400170060ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. /* Package qtsuite allows quicktest to run test suites. A test suite is a value with one or more test methods. For example, the following code defines a suite of test functions that starts an HTTP server before running each test, and tears it down afterwards: type suite struct { url string } func (s *suite) Init(c *qt.C) { hnd := func(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "%s %s", req.Method, req.URL.Path) } srv := httptest.NewServer(http.HandlerFunc(hnd)) c.Cleanup(srv.Close) s.url = srv.URL } func (s *suite) TestGet(c *qt.C) { c.Parallel() resp, err := http.Get(s.url) c.Assert(err, qt.Equals, nil) defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) c.Assert(err, qt.Equals, nil) c.Assert(string(b), qt.Equals, "GET /") } func (s *suite) TestHead(c *qt.C) { c.Parallel() resp, err := http.Head(s.url + "/path") c.Assert(err, qt.Equals, nil) defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) c.Assert(err, qt.Equals, nil) c.Assert(string(b), qt.Equals, "") c.Assert(resp.ContentLength, qt.Equals, int64(10)) } The above code could be invoked from a test function like this: func TestHTTPMethods(t *testing.T) { qtsuite.Run(qt.New(t), &suite{"http://example.com"}) } */ package qtsuite import ( "reflect" "strings" "unicode" "unicode/utf8" qt "github.com/frankban/quicktest" ) // Run runs each test method defined on the given value as a separate // subtest. A test is a method of the form // func (T) TestXxx(*quicktest.C) // where Xxx does not start with a lowercase letter. // // If suite is a pointer, the value pointed to is copied before any // methods are invoked on it; a new copy is made for each test. This // means that it is OK for tests to modify fields in suite concurrently // if desired - it's OK to call c.Parallel(). // // If suite has a method of the form // func (T) Init(*quicktest.C) // this method will be invoked before each test run. func Run(c *qt.C, suite interface{}) { sv := reflect.ValueOf(suite) st := sv.Type() init, hasInit := st.MethodByName("Init") if hasInit && !isValidMethod(init) { c.Fatal("wrong signature for Init, must be Init(*quicktest.C)") } for i := 0; i < st.NumMethod(); i++ { m := st.Method(i) if !isTestMethod(m) { continue } c.Run(m.Name, func(c *qt.C) { if !isValidMethod(m) { c.Fatalf("wrong signature for %s, must be %s(*quicktest.C)", m.Name, m.Name) } sv := sv if st.Kind() == reflect.Ptr { sv1 := reflect.New(st.Elem()) sv1.Elem().Set(sv.Elem()) sv = sv1 } args := []reflect.Value{sv, reflect.ValueOf(c)} if hasInit { init.Func.Call(args) } m.Func.Call(args) }) } } var cType = reflect.TypeOf(&qt.C{}) func isTestMethod(m reflect.Method) bool { if !strings.HasPrefix(m.Name, "Test") { return false } r, n := utf8.DecodeRuneInString(m.Name[4:]) return n == 0 || !unicode.IsLower(r) } func isValidMethod(m reflect.Method) bool { return m.Type.NumIn() == 2 && m.Type.NumOut() == 0 && m.Type.In(1) == cType } quicktest-1.14.0/qtsuite/suite_test.go000066400000000000000000000057171413325465400200470ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package qtsuite_test import ( "bytes" "fmt" "testing" qt "github.com/frankban/quicktest" "github.com/frankban/quicktest/qtsuite" ) func TestRunSuite(t *testing.T) { c := qt.New(t) var calls []call tt := &testingT{} qtsuite.Run(qt.New(tt), testSuite{calls: &calls}) c.Assert(calls, qt.DeepEquals, []call{ {"Test1", 0}, {"Test4", 0}, }) } func TestRunSuiteEmbedded(t *testing.T) { c := qt.New(t) var calls []call tt := &testingT{} suite := struct { testSuite }{testSuite: testSuite{calls: &calls}} qtsuite.Run(qt.New(tt), suite) c.Assert(calls, qt.DeepEquals, []call{ {"Test1", 0}, {"Test4", 0}, }) } func TestRunSuitePtr(t *testing.T) { c := qt.New(t) var calls []call tt := &testingT{} qtsuite.Run(qt.New(tt), &testSuite{calls: &calls}) c.Assert(calls, qt.DeepEquals, []call{ {"Init", 0}, {"Test1", 1}, {"Init", 0}, {"Test4", 1}, }) } type testSuite struct { init int calls *[]call } func (s testSuite) addCall(name string) { *s.calls = append(*s.calls, call{Name: name, Init: s.init}) } func (s *testSuite) Init(*qt.C) { s.addCall("Init") s.init++ } func (s testSuite) Test1(*qt.C) { s.addCall("Test1") } func (s testSuite) Test2() { s.addCall("Test2") } func (s testSuite) Test3(*testing.T) { s.addCall("Test3") } func (s testSuite) Test4(*qt.C) { s.addCall("Test4") } func (s testSuite) Test5(*qt.C) bool { s.addCall("Test5") return false } func (s testSuite) Testa(*qt.C) { s.addCall("Testa") } type call struct { Name string Init int } func TestInvalidInit(t *testing.T) { c := qt.New(t) tt := &testingT{} tc := qt.New(tt) qtsuite.Run(tc, invalidTestSuite{}) c.Assert(tt.fatalString(), qt.Equals, "wrong signature for Init, must be Init(*quicktest.C)") } type invalidTestSuite struct{} func (invalidTestSuite) Init() {} // testingT can be passed to qt.New for testing purposes. type testingT struct { testing.TB errorBuf bytes.Buffer fatalBuf bytes.Buffer subTestResult bool subTestName string subTestT *testing.T } // Error overrides *testing.T.Error so that messages are collected. func (t *testingT) Error(a ...interface{}) { fmt.Fprint(&t.errorBuf, a...) } // Fatal overrides *testing.T.Fatal so that messages are collected and the // goroutine is not killed. func (t *testingT) Fatal(a ...interface{}) { fmt.Fprint(&t.fatalBuf, a...) } // Run overrides *testing.T.Run. func (t *testingT) Run(name string, f func(t *testing.T)) bool { t.subTestName, t.subTestT = name, &testing.T{} ch := make(chan struct{}) // Run the subtest in its own goroutine so that if it calls runtime.GoExit, // we can still return appropriately. go func() { defer close(ch) f(t.subTestT) }() <-ch return t.subTestResult } // errorString returns the error message. func (t *testingT) errorString() string { return t.errorBuf.String() } // fatalString returns the fatal error message. func (t *testingT) fatalString() string { return t.fatalBuf.String() } quicktest-1.14.0/quicktest.go000066400000000000000000000244601413325465400161710ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "fmt" "reflect" "strings" "sync" "testing" ) // Check runs the given check using the provided t and continues execution in // case of failure. For instance: // // qt.Check(t, answer, qt.Equals, 42) // qt.Check(t, got, qt.IsNil, qt.Commentf("iteration %d", i)) // // Additional args (not consumed by the checker), when provided, are included as // comments in the failure output when the check fails. func Check(t testing.TB, got interface{}, checker Checker, args ...interface{}) bool { t.Helper() return New(t).Check(got, checker, args...) } // Assert runs the given check using the provided t and stops execution in case // of failure. For instance: // // qt.Assert(t, got, qt.DeepEquals, []int{42, 47}) // qt.Assert(t, got, qt.ErrorMatches, "bad wolf .*", qt.Commentf("a comment")) // // Additional args (not consumed by the checker), when provided, are included as // comments in the failure output when the check fails. func Assert(t testing.TB, got interface{}, checker Checker, args ...interface{}) bool { t.Helper() return New(t).Assert(got, checker, args...) } // New returns a new checker instance that uses t to fail the test when checks // fail. It only ever calls the Fatal, Error and (when available) Run methods // of t. For instance. // // func TestFoo(t *testing.T) { // t.Run("A=42", func(t *testing.T) { // c := qt.New(t) // c.Assert(a, qt.Equals, 42) // }) // } // // The library already provides some base checkers, and more can be added by // implementing the Checker interface. // // If there is a likelihood that Defer will be called, then // a call to Done should be deferred after calling New. // For example: // // func TestFoo(t *testing.T) { // c := qt.New(t) // defer c.Done() // c.Setenv("HOME", "/non-existent") // c.Assert(os.Getenv("HOME"), qt.Equals, "/non-existent") // }) // // A value of C that's has a non-nil TB field but is otherwise zero is valid. // So: // // c := &qt.C{TB: t} // // is valid a way to create a C value; it's exactly the same as: // // c := qt.New(t) // // Methods on C may be called concurrently, assuming the underlying // `testing.TB` implementation also allows that. func New(t testing.TB) *C { return &C{ TB: t, } } // C is a quicktest checker. It embeds a testing.TB value and provides // additional checking functionality. If an Assert or Check operation fails, it // uses the wrapped TB value to fail the test appropriately. type C struct { testing.TB mu sync.Mutex doneNeeded bool deferred func() format formatFunc } // cleaner is implemented by testing.TB on Go 1.14 and later. type cleaner interface { Cleanup(func()) } // Defer registers a function to be called when c.Done is // called. Deferred functions will be called in last added, first called // order. If c.Done is not called by the end of the test, the test // may panic. Note that if Cleanup is called, there is no // need to call Done. // // Deprecated: in Go >= 1.14 use testing.TB.Cleanup instead. func (c *C) Defer(f func()) { c.mu.Lock() defer c.mu.Unlock() if cleaner, ok := c.TB.(cleaner); ok { // Use TB.Cleanup when available, but add a check // that Done has been called so that we don't run // into unexpected Go version incompatibilities. if c.doneNeeded { // We've already installed the wrapper func that checks for Done // so we can avoid doing it again. cleaner.Cleanup(f) return } c.doneNeeded = true cleaner.Cleanup(func() { c.mu.Lock() doneNeeded := c.doneNeeded c.mu.Unlock() if doneNeeded { panic("Done not called after Defer") } f() }) return } oldDeferred := c.deferred c.deferred = func() { if oldDeferred != nil { defer oldDeferred() } f() } } // Done calls all the functions registered by Defer in reverse // registration order. After it's called, the functions are // unregistered, so calling Done twice will only call them once. // // When a test function is called by Run, Done will be called // automatically on the C value passed into it. // // Deprecated: in Go >= 1.14 this is no longer needed if using // testing.TB.Cleanup. func (c *C) Done() { c.mu.Lock() deferred := c.deferred c.deferred = nil c.doneNeeded = false c.mu.Unlock() if deferred != nil { deferred() } } // SetFormat sets the function used to print values in test failures. // By default Format is used. // Any subsequent subtests invoked with c.Run will also use this function by // default. func (c *C) SetFormat(format func(interface{}) string) { c.mu.Lock() c.format = format c.mu.Unlock() } // getFormat returns the format function // safely acquired under lock. func (c *C) getFormat() func(interface{}) string { c.mu.Lock() defer c.mu.Unlock() return c.format } // Check runs the given check and continues execution in case of failure. // For instance: // // c.Check(answer, qt.Equals, 42) // c.Check(got, qt.IsNil, qt.Commentf("iteration %d", i)) // // Additional args (not consumed by the checker), when provided, are included // as comments in the failure output when the check fails. func (c *C) Check(got interface{}, checker Checker, args ...interface{}) bool { c.TB.Helper() return check(c, checkParams{ fail: c.TB.Error, checker: checker, got: got, args: args, }) } // Assert runs the given check and stops execution in case of failure. // For instance: // // c.Assert(got, qt.DeepEquals, []int{42, 47}) // c.Assert(got, qt.ErrorMatches, "bad wolf .*", qt.Commentf("a comment")) // // Additional args (not consumed by the checker), when provided, are included // as comments in the failure output when the check fails. func (c *C) Assert(got interface{}, checker Checker, args ...interface{}) bool { c.TB.Helper() return check(c, checkParams{ fail: c.TB.Fatal, checker: checker, got: got, args: args, }) } var ( stringType = reflect.TypeOf("") boolType = reflect.TypeOf(true) tbType = reflect.TypeOf(new(testing.TB)).Elem() ) // Run runs f as a subtest of t called name. It's a wrapper around // the Run method of c.TB that provides the quicktest checker to f. When // the function completes, c.Done will be called to run any // functions registered with c.Defer. // // c.TB must implement a Run method of the following form: // // Run(string, func(T)) bool // // where T is any type that is assignable to testing.TB. // Implementations include *testing.T, *testing.B and *C itself. // // The TB field in the subtest will hold the value passed // by Run to its argument function. // // func TestFoo(t *testing.T) { // c := qt.New(t) // c.Run("A=42", func(c *qt.C) { // // This assertion only stops the current subtest. // c.Assert(a, qt.Equals, 42) // }) // } // // A panic is raised when Run is called and the embedded concrete type does not // implement a Run method with a correct signature. func (c *C) Run(name string, f func(c *C)) bool { badType := func(m string) { panic(fmt.Sprintf("cannot execute Run with underlying concrete type %T (%s)", c.TB, m)) } m := reflect.ValueOf(c.TB).MethodByName("Run") if !m.IsValid() { // c.TB doesn't implement a Run method. badType("no Run method") } mt := m.Type() if mt.NumIn() != 2 || mt.In(0) != stringType || mt.NumOut() != 1 || mt.Out(0) != boolType { // The Run method doesn't have the right argument counts and types. badType("wrong argument count for Run method") } farg := mt.In(1) if farg.Kind() != reflect.Func || farg.NumIn() != 1 || farg.NumOut() != 0 || !farg.In(0).AssignableTo(tbType) { // The first argument to the Run function arg isn't right. badType("bad first argument type for Run method") } fv := reflect.MakeFunc(farg, func(args []reflect.Value) []reflect.Value { c2 := New(args[0].Interface().(testing.TB)) defer c2.Done() c2.SetFormat(c.getFormat()) f(c2) return nil }) return m.Call([]reflect.Value{reflect.ValueOf(name), fv})[0].Interface().(bool) } // Parallel signals that this test is to be run in parallel with (and only with) other parallel tests. // It's a wrapper around *testing.T.Parallel. // // A panic is raised when Parallel is called and the embedded concrete type does not // implement Parallel, for instance if TB's concrete type is a benchmark. func (c *C) Parallel() { p, ok := c.TB.(interface { Parallel() }) if !ok { panic(fmt.Sprintf("cannot execute Parallel with underlying concrete type %T", c.TB)) } p.Parallel() } // check performs the actual check with the provided params. // In case of failure p.fail is called. In the fail report values are formatted // using p.format. func check(c *C, p checkParams) bool { c.TB.Helper() rp := reportParams{ got: p.got, args: p.args, format: c.getFormat(), } if rp.format == nil { // No format set; use the default: Format. rp.format = Format } // Allow checkers to annotate messages. note := func(key string, value interface{}) { rp.notes = append(rp.notes, note{ key: key, value: value, }) } // Ensure that we have a checker. if p.checker == nil { p.fail(report(BadCheckf("nil checker provided"), rp)) return false } // Extract a comment if it has been provided. rp.argNames = p.checker.ArgNames() wantNumArgs := len(rp.argNames) - 1 if len(p.args) > 0 { if comment, ok := p.args[len(p.args)-1].(Comment); ok { rp.comment = comment rp.args = p.args[:len(p.args)-1] } } // Validate that we have the correct number of arguments. if gotNumArgs := len(rp.args); gotNumArgs != wantNumArgs { if gotNumArgs > 0 { note("got args", rp.args) } if wantNumArgs > 0 { note("want args", Unquoted(strings.Join(rp.argNames[1:], ", "))) } var prefix string if gotNumArgs > wantNumArgs { prefix = "too many arguments provided to checker" } else { prefix = "not enough arguments provided to checker" } p.fail(report(BadCheckf("%s: got %d, want %d", prefix, gotNumArgs, wantNumArgs), rp)) return false } // Execute the check and report the failure if necessary. if err := p.checker.Check(p.got, p.args, note); err != nil { p.fail(report(err, rp)) return false } return true } // checkParams holds parameters for executing a check. type checkParams struct { fail func(...interface{}) checker Checker got interface{} args []interface{} } quicktest-1.14.0/quicktest_test.go000066400000000000000000000367031413325465400172330ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "bytes" "errors" "fmt" "strings" "testing" qt "github.com/frankban/quicktest" ) var _ testing.TB = (*qt.C)(nil) var cTests = []struct { about string checker qt.Checker got interface{} args []interface{} format func(interface{}) string expectedFailure string }{{ about: "success", checker: qt.Equals, got: 42, args: []interface{}{42}, }, { about: "failure", checker: qt.Equals, got: "42", args: []interface{}{"47"}, expectedFailure: ` error: values are not equal got: "42" want: "47" `, }, { about: "failure with % signs", checker: qt.Equals, got: "42%x", args: []interface{}{"47%y"}, expectedFailure: ` error: values are not equal got: "42%x" want: "47%y" `, }, { about: "failure with comment", checker: qt.Equals, got: true, args: []interface{}{false, qt.Commentf("apparently %v != %v", true, false)}, expectedFailure: ` error: values are not equal comment: apparently true != false got: bool(true) want: bool(false) `, }, { about: "another failure with comment", checker: qt.IsNil, got: 42, args: []interface{}{qt.Commentf("bad wolf: %d", 42)}, expectedFailure: ` error: got non-nil value comment: bad wolf: 42 got: int(42) `, }, { about: "failure with constant comment", checker: qt.IsNil, got: "something", args: []interface{}{qt.Commentf("these are the voyages")}, expectedFailure: ` error: got non-nil value comment: these are the voyages got: "something" `, }, { about: "failure with empty comment", checker: qt.IsNil, got: 47, args: []interface{}{qt.Commentf("")}, expectedFailure: ` error: got non-nil value got: int(47) `, }, { about: "nil checker", expectedFailure: ` error: bad check: nil checker provided `, }, { about: "not enough arguments", checker: qt.Equals, got: 42, args: []interface{}{}, expectedFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 want args: want `, }, { about: "not enough arguments with comment", checker: qt.DeepEquals, got: 42, args: []interface{}{qt.Commentf("test %d", 0)}, expectedFailure: ` error: bad check: not enough arguments provided to checker: got 0, want 1 comment: test 0 want args: want `, }, { about: "too many arguments", checker: qt.Matches, got: 42, args: []interface{}{42, 47}, expectedFailure: ` error: bad check: too many arguments provided to checker: got 2, want 1 got args: []interface {}{ int(42), int(47), } want args: regexp `, }, { about: "really too many arguments", checker: qt.DeepEquals, got: 42, args: []interface{}{42, 47, nil, "stop"}, expectedFailure: ` error: bad check: too many arguments provided to checker: got 4, want 1 got args: []interface {}{ int(42), int(47), nil, "stop", } want args: want `, }, { about: "too many arguments with comment", checker: qt.IsNil, got: 42, args: []interface{}{nil, qt.Commentf("these are the voyages")}, expectedFailure: ` error: bad check: too many arguments provided to checker: got 1, want 0 comment: these are the voyages got args: []interface {}{ nil, } `, }, { about: "many arguments and notes", checker: &testingChecker{ argNames: []string{"arg1", "arg2", "arg3"}, addNotes: func(note func(key string, value interface{})) { note("note1", "these") note("note2", qt.Unquoted("are")) note("note3", "the") note("note4", "voyages") note("note5", true) }, err: errors.New("bad wolf"), }, got: 42, args: []interface{}{"val2", "val3"}, expectedFailure: ` error: bad wolf note1: "these" note2: are note3: "the" note4: "voyages" note5: bool(true) arg1: int(42) arg2: "val2" arg3: "val3" `, }, { about: "many arguments and notes with the same value", checker: &testingChecker{ argNames: []string{"arg1", "arg2", "arg3", "arg4"}, addNotes: func(note func(key string, value interface{})) { note("note1", "value1") note("note2", []int{42}) note("note3", "value1") note("note4", nil) }, err: errors.New("bad wolf"), }, got: "value1", args: []interface{}{"value1", []int{42}, nil}, expectedFailure: ` error: bad wolf note1: "value1" note2: []int{42} note3: note4: nil arg1: arg2: arg3: arg4: `, }, { about: "many arguments and notes with custom format function", checker: &testingChecker{ argNames: []string{"arg1", "arg2", "arg3"}, addNotes: func(note func(key string, value interface{})) { note("note1", "these") note("note2", qt.Unquoted("are")) note("note3", "the") note("note4", "voyages") note("note5", true) }, err: errors.New("bad wolf"), }, got: 42, args: []interface{}{"val2", "val3"}, format: func(v interface{}) string { return fmt.Sprintf("bad wolf %v", v) }, expectedFailure: ` error: bad wolf note1: bad wolf these note2: are note3: bad wolf the note4: bad wolf voyages note5: bad wolf true arg1: bad wolf 42 arg2: bad wolf val2 arg3: bad wolf val3 `, }, { about: "bad check with notes", checker: &testingChecker{ argNames: []string{"got", "want"}, addNotes: func(note func(key string, value interface{})) { note("note", 42) }, err: qt.BadCheckf("bad wolf"), }, got: 42, args: []interface{}{"want"}, expectedFailure: ` error: bad check: bad wolf note: int(42) `, }, { about: "silent failure with notes", checker: &testingChecker{ argNames: []string{"got", "want"}, addNotes: func(note func(key string, value interface{})) { note("note1", "first note") note("note2", qt.Unquoted("second note")) }, err: qt.ErrSilent, }, got: 42, args: []interface{}{"want"}, expectedFailure: ` note1: "first note" note2: second note `, }} func TestCAssertCheck(t *testing.T) { for _, test := range cTests { t.Run("Assert: "+test.about, func(t *testing.T) { if test.format != nil { t.Skip("changing format not supported when using qt.Assert directly") } tt := &testingT{} ok := qt.Assert(tt, test.got, test.checker, test.args...) checkResult(t, ok, tt.fatalString(), test.expectedFailure) if tt.errorString() != "" { t.Fatalf("no error messages expected, but got %q", tt.errorString()) } }) t.Run("Check: "+test.about, func(t *testing.T) { if test.format != nil { t.Skip("changing format not supported when using qt.Check directly") } tt := &testingT{} ok := qt.Check(tt, test.got, test.checker, test.args...) checkResult(t, ok, tt.errorString(), test.expectedFailure) if tt.fatalString() != "" { t.Fatalf("no fatal messages expected, but got %q", tt.fatalString()) } }) t.Run("c.Assert: "+test.about, func(t *testing.T) { tt := &testingT{} c := qt.New(tt) if test.format != nil { c.SetFormat(test.format) } ok := c.Assert(test.got, test.checker, test.args...) checkResult(t, ok, tt.fatalString(), test.expectedFailure) if tt.errorString() != "" { t.Fatalf("no error messages expected, but got %q", tt.errorString()) } }) t.Run("c.Check: "+test.about, func(t *testing.T) { tt := &testingT{} c := qt.New(tt) if test.format != nil { c.SetFormat(test.format) } ok := c.Check(test.got, test.checker, test.args...) checkResult(t, ok, tt.errorString(), test.expectedFailure) if tt.fatalString() != "" { t.Fatalf("no fatal messages expected, but got %q", tt.fatalString()) } }) } } func TestCRunSuccess(t *testing.T) { tt := &testingT{} c := qt.New(tt) var run bool subTestName := "my test" ok := c.Run(subTestName, func(innerC *qt.C) { run = true if innerC == c { t.Fatal("subtest C: same instance provided") } if innerC.TB != tt.subTestT { t.Fatalf("subtest testing object: got %p, want %p", innerC.TB, tt.subTestT) } if tt.subTestName != subTestName { t.Fatalf("subtest name: got %q, want %q", tt.subTestName, subTestName) } }) assertBool(t, run, true) assertBool(t, ok, false) // Simulate a test success. tt.subTestResult = true ok = c.Run(subTestName, func(innerC *qt.C) {}) assertBool(t, ok, true) } func TestCRunOnBenchmark(t *testing.T) { called := false testing.Benchmark(func(b *testing.B) { c := qt.New(b) c.Run("c", func(c *qt.C) { b1, ok := c.TB.(*testing.B) if !ok { t.Errorf("c.TB is type %T not *testing.B", c.TB) return } if b1 == b { t.Errorf("c.TB hasn't been given a new B value") return } called = true }) }) if !called { t.Fatalf("sub-benchmark was never called") } } // wrongRun1 has Run method with wrong arg count. type wrongRun1 struct { testing.TB } func (wrongRun1) Run() {} // wrongRun2 has no Run method. type wrongRun2 struct { testing.TB } // wrongRun3 has Run method that takes a type not // assignable to testing.TB. type wrongRun3 struct { testing.TB } func (wrongRun3) Run(string, func(string)) bool { return false } // wrongRun4 has Run method that doesn't return bool. type wrongRun4 struct { testing.TB } func (wrongRun4) Run(string, func(*testing.T)) {} var CRunPanicTests = []struct { tb testing.TB expectPanic string }{{ tb: wrongRun1{}, expectPanic: "wrong argument count for Run method", }, { tb: wrongRun2{}, expectPanic: "no Run method", }, { tb: wrongRun3{}, expectPanic: "bad first argument type for Run method", }, { tb: wrongRun4{}, expectPanic: "wrong argument count for Run method", }} func TestCRunPanic(t *testing.T) { for _, test := range CRunPanicTests { t.Run(fmt.Sprintf("%T", test.tb), func(t *testing.T) { c := qt.New(test.tb) defer func() { got := recover() want := fmt.Sprintf( "cannot execute Run with underlying concrete type %T (%s)", test.tb, test.expectPanic, ) if got != want { t.Fatalf("unexpected panic recover message; got %q want %q", got, want) } }() c.Run("panic", func(innerC *qt.C) {}) }) } } func TestCRunFormat(t *testing.T) { tt, innerTT := &testingT{}, &testingT{} c := qt.New(tt) c.SetFormat(func(v interface{}) string { return fmt.Sprintf("myfmt(%v)", v) }) c.Run("my test", func(innerC *qt.C) { innerC.TB = innerTT innerC.Check(42, qt.Equals, nil) }) assertPrefix(t, innerTT.errorString(), ` error: values are not equal got: myfmt(42) want: myfmt() `) } func TestHelper(t *testing.T) { tt := &testingT{} qt.Assert(tt, true, qt.IsFalse) if tt.helperCalls != 3 { t.Fatalf("want 3 calls (Assert, c.Assert, check), got %d", tt.helperCalls) } } func TestCHelper(t *testing.T) { tt := &testingT{} c := qt.New(tt) c.Assert(true, qt.IsFalse) if tt.helperCalls != 2 { t.Fatalf("want 2 calls (c.Assert, check), got %d", tt.helperCalls) } } func TestCParallel(t *testing.T) { tt := &testingT{} c := qt.New(tt) c.Parallel() if !tt.parallel { t.Fatalf("parallel not called") } } func TestCParallelPanic(t *testing.T) { c := qt.New(&testing.B{}) defer func() { r := recover() if r != "cannot execute Parallel with underlying concrete type *testing.B" { t.Fatalf("unexpected panic recover: %v", r) } }() c.Parallel() } func TestCDefer(t *testing.T) { c := qt.New(t) var defers []int c.Run("subtest", func(c *qt.C) { c.Defer(func() { defers = append(defers, 1) }) c.Defer(func() { defers = append(defers, 2) }) // Calling Done twice should not do anything more. c.Done() }) c.Assert(defers, qt.DeepEquals, []int{2, 1}) } func TestCDeferCalledEvenAfterGoexit(t *testing.T) { // The testing package uses runtime.Goexit on // assertion failure, so check that defers are still // called in that case. c := qt.New(t) defers := 0 c.Run("subtest", func(c *qt.C) { c.Defer(func() { defers++ }) c.Defer(func() { c.SkipNow() }) }) c.Assert(defers, qt.Equals, 1) } func TestCRunDefer(t *testing.T) { c := qt.New(t) defers := 0 c.Run("subtest", func(c *qt.C) { c.Run("x", func(c *qt.C) { c.Defer(func() { defers++ }) }) }) c.Assert(defers, qt.Equals, 1) } type customT struct { *testing.T data int } func (t *customT) Run(name string, f func(*customT)) bool { return t.T.Run(name, func(t1 *testing.T) { f(&customT{t1, t.data}) }) } func TestCRunCustomType(t *testing.T) { ct := &customT{t, 99} c := qt.New(ct) called := 0 c.Run("test", func(c *qt.C) { called++ ct1, ok := c.TB.(*customT) if !ok { t.Error("TB isn't expected type") } if ct1.data != ct.data { t.Errorf("data not copied correctly; got %v want %v", ct1.data, ct.data) } if ct1 == ct { t.Errorf("old instance passed, not new") } }) if called != 1 { t.Fatalf("subtest was called %d times, not once", called) } } func checkResult(t *testing.T, ok bool, got, want string) { if want != "" { assertPrefix(t, got, want+"stack:\n") assertBool(t, ok, false) return } if got != "" { t.Fatalf("output:\ngot %q\nwant empty", got) } assertBool(t, ok, true) } // testingT can be passed to qt.New for testing purposes. type testingT struct { testing.TB errorBuf bytes.Buffer fatalBuf bytes.Buffer subTestResult bool subTestName string subTestT *testing.T helperCalls int parallel bool } // Error overrides testing.TB.Error so that messages are collected. func (t *testingT) Error(a ...interface{}) { fmt.Fprint(&t.errorBuf, a...) } // Fatal overrides testing.TB.Fatal so that messages are collected and the // goroutine is not killed. func (t *testingT) Fatal(a ...interface{}) { fmt.Fprint(&t.fatalBuf, a...) } // Parallel overrides testing.TB.Parallel in order to record the call. func (t *testingT) Parallel() { t.parallel = true } // Helper overrides testing.TB.Helper in order to count calls. func (t *testingT) Helper() { t.helperCalls += 1 } // Fatal overrides *testing.T.Fatal so that messages are collected and the // goroutine is not killed. func (t *testingT) Run(name string, f func(t *testing.T)) bool { t.subTestName, t.subTestT = name, &testing.T{} f(t.subTestT) return t.subTestResult } // errorString returns the error message. func (t *testingT) errorString() string { return t.errorBuf.String() } // fatalString returns the fatal error message. func (t *testingT) fatalString() string { return t.fatalBuf.String() } // assertPrefix fails if the got value does not have the given prefix. func assertPrefix(t testing.TB, got, prefix string) { t.Helper() if prefix == "" { t.Fatal("prefix: empty value provided") } if !strings.HasPrefix(got, prefix) { t.Fatalf(`prefix: got %q want %q -------------------- got -------------------- %s -------------------- want ------------------- %s ---------------------------------------------`, got, prefix, got, prefix) } } // assertBool fails if the given boolean values don't match. func assertBool(t testing.TB, got, want bool) { t.Helper() if got != want { t.Fatalf("bool:\ngot %v\nwant %v", got, want) } } // testingChecker is a quicktest.Checker used in tests. It receives the // provided argNames, adds notes via the provided addNotes function, and when // the check is run the provided error is returned. type testingChecker struct { argNames []string addNotes func(note func(key string, value interface{})) err error } // Check implements quicktest.Checker by returning the stored error. func (c *testingChecker) Check(got interface{}, args []interface{}, note func(key string, value interface{})) error { if c.addNotes != nil { c.addNotes(note) } return c.err } // Info implements quicktest.Checker by returning the stored args. func (c *testingChecker) ArgNames() []string { return c.argNames } quicktest-1.14.0/race_test.go000066400000000000000000000036271413325465400161300ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "sync" "sync/atomic" "testing" qt "github.com/frankban/quicktest" ) func TestConcurrentMethods(t *testing.T) { // This test is designed to be run with the race // detector enabled. It checks that C methods // are safe to call concurrently. // N holds the number of iterations to run any given // operation concurrently with the others. const N = 100 var x, y int32 c := qt.New(dummyT{t}) c.Run("subtest", func(c *qt.C) { var wg sync.WaitGroup // start calls f in two goroutines, each // running it N times. // All the goroutines get started before we actually // start them running, so that the race detector // has a better chance of catching issues. gogogo := make(chan struct{}) start := func(f func()) { repeat := func() { defer wg.Done() <-gogogo for i := 0; i < N; i++ { f() } } wg.Add(2) go repeat() go repeat() } start(func() { c.Defer(func() { atomic.AddInt32(&x, 1) }) c.Defer(func() { atomic.AddInt32(&y, 1) }) }) start(func() { c.Done() }) start(func() { c.SetFormat(func(v interface{}) string { return "x" }) }) start(func() { // Do an assert to exercise the formatter. c.Check(true, qt.Equals, false) }) start(func() { c.Run("", func(c *qt.C) {}) }) close(gogogo) wg.Wait() }) // Check that all the defer functions ran OK. if x != N*2 || y != N*2 { t.Fatalf("unexpected x, y counts; got %d, %d; want %d, %d", x, y, N*2, N*2) } } // dummyT wraps a *testing.T value suitable // for TestConcurrentMethods so that calling Error // won't fail the test and that it implements // Run correctly. type dummyT struct { *testing.T } func (dummyT) Error(...interface{}) {} func (t dummyT) Run(name string, f func(t dummyT)) bool { return t.T.Run(name, func(t *testing.T) { f(dummyT{t}) }) } quicktest-1.14.0/report.go000066400000000000000000000126141413325465400154660ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest import ( "bytes" "fmt" "go/ast" "go/parser" "go/printer" "go/token" "io" "reflect" "runtime" "strings" ) // reportParams holds parameters for reporting a test error. type reportParams struct { // argNames holds the names for the arguments passed to the checker. argNames []string // got holds the value that was checked. got interface{} // args holds all other arguments (if any) provided to the checker. args []interface{} // comment optionally holds the comment passed when performing the check. comment Comment // notes holds notes added while doing the check. notes []note // format holds the format function that must be used when outputting // values. format formatFunc } // Unquoted indicates that the string must not be pretty printed in the failure // output. This is useful when a checker calls note and does not want the // provided value to be quoted. type Unquoted string // report generates a failure report for the given error, optionally including // in the output the checker arguments, comment and notes included in the // provided report parameters. func report(err error, p reportParams) string { var buf bytes.Buffer buf.WriteByte('\n') writeError(&buf, err, p) writeStack(&buf) return buf.String() } // writeError writes a pretty formatted output of the given error using the // provided report parameters. func writeError(w io.Writer, err error, p reportParams) { values := make(map[string]string) printPair := func(key string, value interface{}) { fmt.Fprintln(w, key+":") var v string if u, ok := value.(Unquoted); ok { v = string(u) } else { v = p.format(value) } if k := values[v]; k != "" { fmt.Fprint(w, prefixf(prefix, "", k)) return } values[v] = key fmt.Fprint(w, prefixf(prefix, "%s", v)) } // Write the checker error. if err != ErrSilent { printPair("error", Unquoted(err.Error())) } // Write the comment if provided. if comment := p.comment.String(); comment != "" { printPair("comment", Unquoted(comment)) } // Write notes if present. for _, n := range p.notes { printPair(n.key, n.value) } if IsBadCheck(err) || err == ErrSilent { // For errors in the checker invocation or for silent errors, do not // show output from args. return } // Write provided args. for i, arg := range append([]interface{}{p.got}, p.args...) { printPair(p.argNames[i], arg) } } // writeStack writes the traceback information for the current failure into the // provided writer. func writeStack(w io.Writer) { fmt.Fprintln(w, "stack:") pc := make([]uintptr, 8) sg := &stmtGetter{ fset: token.NewFileSet(), files: make(map[string]*ast.File, 8), config: &printer.Config{ Mode: printer.UseSpaces, Tabwidth: 4, }, } runtime.Callers(5, pc) frames := runtime.CallersFrames(pc) thisPackage := reflect.TypeOf(C{}).PkgPath() + "." for { frame, more := frames.Next() if strings.HasPrefix(frame.Function, "testing.") { // Stop before getting back to stdlib test runner calls. break } if fname := strings.TrimPrefix(frame.Function, thisPackage); fname != frame.Function { if ast.IsExported(fname) { // Continue without printing frames for quicktest exported API. continue } // Stop when entering quicktest internal calls. // This is useful for instance when using qtsuite. break } fmt.Fprint(w, prefixf(prefix, "%s:%d", frame.File, frame.Line)) if strings.HasSuffix(frame.File, ".go") { stmt, err := sg.Get(frame.File, frame.Line) if err != nil { fmt.Fprint(w, prefixf(prefix+prefix, "<%s>", err)) } else { fmt.Fprint(w, prefixf(prefix+prefix, "%s", stmt)) } } if !more { // There are no more callers. break } } } type stmtGetter struct { fset *token.FileSet files map[string]*ast.File config *printer.Config } // Get returns the lines of code of the statement at the given file and line. func (sg *stmtGetter) Get(file string, line int) (string, error) { f := sg.files[file] if f == nil { var err error f, err = parser.ParseFile(sg.fset, file, nil, parser.ParseComments) if err != nil { return "", fmt.Errorf("cannot parse source file: %s", err) } sg.files[file] = f } var stmt string ast.Inspect(f, func(n ast.Node) bool { if n == nil || stmt != "" { return false } pos := sg.fset.Position(n.Pos()).Line end := sg.fset.Position(n.End()).Line // Go < v1.9 reports the line where the statements ends, not the line // where it begins. if line == pos || line == end { var buf bytes.Buffer // TODO: include possible comment after the statement. sg.config.Fprint(&buf, sg.fset, &printer.CommentedNode{ Node: n, Comments: f.Comments, }) stmt = buf.String() return false } return pos < line && line <= end }) return stmt, nil } // prefixf formats the given string with the given args. It also inserts the // final newline if needed and indentation with the given prefix. func prefixf(prefix, format string, args ...interface{}) string { var buf []byte s := strings.TrimSuffix(fmt.Sprintf(format, args...), "\n") for _, line := range strings.Split(s, "\n") { buf = append(buf, prefix...) buf = append(buf, line...) buf = append(buf, '\n') } return string(buf) } // note holds a key/value annotation. type note struct { key string value interface{} } // prefix is the string used to indent blocks of output. const prefix = " " quicktest-1.14.0/report_test.go000066400000000000000000000060321413325465400165220ustar00rootroot00000000000000// Licensed under the MIT license, see LICENCE file for details. package quicktest_test import ( "runtime" "strings" "testing" qt "github.com/frankban/quicktest" ) // The tests in this file rely on their own source code lines. func TestReportOutput(t *testing.T) { tt := &testingT{} c := qt.New(tt) c.Assert(42, qt.Equals, 47) want := ` error: values are not equal got: int(42) want: int(47) stack: $file:18 c.Assert(42, qt.Equals, 47) ` assertReport(t, tt, want) } func f1(c *qt.C) { f2(c) } func f2(c *qt.C) { c.Assert(42, qt.IsNil) // Real assertion here! } func TestIndirectReportOutput(t *testing.T) { tt := &testingT{} c := qt.New(tt) f1(c) want := ` error: got non-nil value got: int(42) stack: $file:38 c.Assert(42, qt.IsNil) $file:34 f2(c) $file:44 f1(c) ` assertReport(t, tt, want) } func TestMultilineReportOutput(t *testing.T) { tt := &testingT{} c := qt.New(tt) c.Assert( "this string", // Comment 1. qt.Equals, "another string", qt.Commentf("a comment"), // Comment 2. ) // Comment 3. want := ` error: values are not equal comment: a comment got: "this string" want: "another string" stack: $file:64 c.Assert( "this string", // Comment 1. qt.Equals, "another string", qt.Commentf("a comment"), // Comment 2. ) ` assertReport(t, tt, want) } func TestCmpReportOutput(t *testing.T) { tt := &testingT{} c := qt.New(tt) gotExamples := []*reportExample{{ AnInt: 42, }, { AnInt: 47, }, { AnInt: 1, }, { AnInt: 2, }} wantExamples := []*reportExample{{ AnInt: 42, }, { AnInt: 47, }, { AnInt: 2, }, { AnInt: 1, }, {}} 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}, &{AnInt: 47}, + &{AnInt: 2}, &{AnInt: 1}, - &{AnInt: 2}, + &{}, } stack: $file:113 c.Assert(gotExamples, checker, wantExamples) ` assertReport(t, tt, want) } func TestTopLevelAssertReportOutput(t *testing.T) { tt := &testingT{} qt.Assert(tt, 42, qt.Equals, 47) want := ` error: values are not equal got: int(42) want: int(47) stack: $file:135 qt.Assert(tt, 42, qt.Equals, 47) ` assertReport(t, tt, want) } func assertReport(t *testing.T, tt *testingT, want string) { 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. _, file, _, ok := runtime.Caller(0) assertBool(t, ok, true) want = strings.Replace(want, "$file", file, -1) if got != want { t.Fatalf(`failure: %q %q ------------------------------ got ------------------------------ %s------------------------------ want ----------------------------- %s-----------------------------------------------------------------`, got, want, got, want) } } type reportExample struct { AnInt int }