pax_global_header00006660000000000000000000000064135606622430014521gustar00rootroot0000000000000052 comment=cf080876c1af3543cad4756668bf4f4b7465b73c check-1.2.1/000077500000000000000000000000001356066224300125775ustar00rootroot00000000000000check-1.2.1/.circleci/000077500000000000000000000000001356066224300144325ustar00rootroot00000000000000check-1.2.1/.circleci/config.yml000066400000000000000000000030161356066224300164220ustar00rootroot00000000000000version: 2.1 jobs: build: docker: - image: circleci/golang:1 environment: GOLANGCI_LINT_VER: 1.20.0 steps: - checkout - run: name: Initialize command: | rmdir /go/*/ # fix owner/permission env | grep _VER | sort > /tmp/tools.ver - restore_cache: keys: - v2-{{ checksum "/tmp/tools.ver" }}-{{ checksum "go.mod" }}-{{ .Branch }} - v2-{{ checksum "/tmp/tools.ver" }}-{{ checksum "go.mod" }}- - v2-{{ checksum "/tmp/tools.ver" }}- - run: name: Install tools command: | cd / golangci-lint --version | tee /dev/stderr | grep -wq $GOLANGCI_LINT_VER || curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b /go/bin v$GOLANGCI_LINT_VER go get -v github.com/mattn/goveralls - run: go test -mod=readonly -v -race ./... - run: golangci-lint run - run: goveralls -service=circle-ci -flags -tags=integration - save_cache: when: always key: v2-{{ checksum "/tmp/tools.ver" }}-{{ checksum "go.mod" }}-{{ .Branch }} paths: - /go/bin/ - /go/pkg/ - /go/src/ - ~/.cache/go-build/ check-1.2.1/.golangci.yml000066400000000000000000000302761356066224300151730ustar00rootroot00000000000000# This file contains all available configuration options # with their default values. # options for analysis running run: # default concurrency is a available CPU number # concurrency: 4 # timeout for analysis, e.g. 30s, 5m, default is 1m # timeout: 1m # exit code when at least one issue was found, default is 1 # issues-exit-code: 1 # include test files or not, default is true # tests: true # list of build tags, all linters use it. Default is empty list. build-tags: - integration # which dirs to skip: they won't be analyzed; # can use regexp here: generated.*, regexp is applied on full path; # default value is empty list, but next dirs are always skipped independently # from this option's value: # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ # skip-dirs: # - src/external_libs # - autogenerated_by_my_lib # which files to skip: they will be analyzed, but issues from them # won't be reported. Default value is empty list, but there is # no need to include all autogenerated files, we confidently recognize # autogenerated files. If it's not please let us know. # skip-files: # - "\\.\\w+\\.go$" # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": # If invoked with -mod=readonly, the go command is disallowed from the implicit # automatic updating of go.mod described above. Instead, it fails when any changes # to go.mod are needed. This setting is most useful to check that go.mod does # not need updates, such as in a continuous integration and testing system. # If invoked with -mod=vendor, the go command assumes that the vendor # directory holds the correct copies of dependencies and ignores # the dependency descriptions in go.mod. modules-download-mode: readonly # output configuration options output: # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" # format: colored-line-number # print lines of code with issue, default is true # print-issued-lines: true # print linter name in the end of issue text, default is true # print-linter-name: true # all available settings of specific linters linters-settings: errcheck: # report about not checking of errors in type assetions: `a := b.(MyStruct)`; # default is false: such cases aren't reported by default. # check-type-assertions: false # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; # default is false: such cases aren't reported by default. # check-blank: false # [deprecated] comma-separated list of pairs of the form pkg:regex # the regex is used to ignore names within pkg. (default "fmt:.*"). # see https://github.com/kisielk/errcheck#the-deprecated-method for details # ignore: fmt:.*,io/ioutil:^Read.* # path to a file containing a list of functions to exclude from checking # see https://github.com/kisielk/errcheck#excluding-functions for details # exclude: .errcheck.excludes funlen: lines: 60 statements: 40 govet: # report about shadowed variables check-shadowing: true # settings per analyzer settings: printf: # analyzer name, run `go tool vet help` to see all analyzers funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer # - (github.com/powerman/structlog.Logger).Fatalf # - (github.com/powerman/structlog.Logger).Panicf # - (github.com/powerman/structlog.Logger).Printf # enable or disable analyzers by name # enable: # - atomicalign enable-all: true # disable: # - shadow # disable-all: false golint: # minimal confidence for issues, default is 0.8 # min-confidence: 0.8 gofmt: # simplify code: gofmt with `-s` option, true by default # simplify: true goimports: # put imports beginning with prefix after 3rd-party packages; # it's a comma-separated list of prefixes # local-prefixes: github.com/powerman gocyclo: # minimal code complexity to report, 30 by default (but we recommend 10-20) min-complexity: 15 gocognit: # minimal code complexity to report, 30 by default (but we recommend 10-20) min-complexity: 20 maligned: # print struct with more effective memory layout or not, false by default suggest-new: true dupl: # tokens count to trigger issue, 150 by default threshold: 100 goconst: # minimal length of string constant, 3 by default # min-len: 3 # minimal occurrences count to trigger, 3 by default # min-occurrences: 3 depguard: list-type: blacklist include-go-root: true packages: - log packages-with-error-messages: # specify an error message to output when a blacklisted package is used log: "logging is allowed only by github.com/powerman/structlog" misspell: # Correct spellings using locale preferences for US or UK. # Default is to use a neutral variety of English. # Setting locale to US will correct the British spelling of 'colour' to 'color'. # locale: US # ignore-words: # - someword lll: # max line length, lines longer will be reported. Default is 120. # '\t' is counted as 1 character by default, and can be changed with the tab-width option # line-length: 120 # tab width in spaces. Default to 1. # tab-width: 1 unused: # treat code as a program (not a library) and report unused exported identifiers; default is false. # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: # if it's called for subdir of a project it can't find funcs usages. All text editor integrations # with golangci-lint call it on a directory with the changed file. # check-exported: false unparam: # Inspect exported functions, default is false. Set to true if no external program/library imports your code. # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: # if it's called for subdir of a project it can't find external interfaces. All text editor integrations # with golangci-lint call it on a directory with the changed file. # check-exported: false nakedret: # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 # max-func-lines: 30 prealloc: # XXX: we don't recommend using this linter before doing performance profiling. # For most programs usage of prealloc will be a premature optimization. # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. # True by default. # simple: true # range-loops: true # Report preallocation suggestions on range loops, true by default # for-loops: false # Report preallocation suggestions on for loops, false by default gocritic: # Which checks should be enabled; can't be combined with 'disabled-checks'; # See https://go-critic.github.io/overview#checks-overview # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` # By default list of stable checks is used. # enabled-checks: # - rangeValCopy # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty disabled-checks: - hugeParam # premature optimization - paramTypeCombine # questionable - ptrToRefParam - typeUnparen # false positive - yodaStyleExpr # questionable # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". enabled-tags: - diagnostic - style - performance - experimental - opinionated settings: # settings passed to gocritic captLocal: # must be valid enabled check name paramsOnly: true rangeValCopy: sizeThreshold: 32 godox: # report any comments starting with keywords, this is useful for TODO or FIXME comments that # might be left in the code accidentally and should be resolved before merging keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting - BUG # marks issues that should be moved to issue tracker before merging - FIXME # marks issues that should be resolved before merging dogsled: # checks assignments with too many blank identifiers; default is 2 max-blank-identifiers: 2 whitespace: multi-if: false # Enforces newlines (or comments) after every multi-line if statement multi-func: false # Enforces newlines (or comments) after every multi-line function signature wsl: # If true append is only allowed to be cuddled if appending value is # matching variables, fields or types on line above. Default is true. strict-append: true # Allow calls and assignments to be cuddled as long as the lines have any # matching variables, fields or types. Default is true. allow-assign-and-call: true # Allow multiline assignments to be cuddled. Default is true. allow-multiline-assign: true linters: # enable: # - megacheck # - govet enable-all: true disable: - dogsled # questionable - dupl - gochecknoglobals - gochecknoinits - lll # - misspell - prealloc - wsl # questionable # disable-all: false # presets: # - bugs # - unused fast: false issues: # List of regexps of issue texts to exclude, empty list by default. # But independently from this option we use default exclude patterns, # it can be disabled by `exclude-use-default: false`. To list all # excluded by default patterns execute `golangci-lint run --help` exclude: - "declaration of \"(log|err|ctx)\" shadows" - "Potential file inclusion via variable" # [DEFAULT] gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)' # Excluding configuration per-path, per-linter, per-text and per-source exclude-rules: # Exclude some linters from running on tests files. - path: _test\.go|testing\.go linters: - bodyclose - dupl - errcheck - funlen - gochecknoglobals - gochecknoinits - gocyclo - gosec - maligned - scopelint # Ease some gocritic warnings on test files. - path: _test\.go|testing\.go text: "(unnamedResult|exitAfterDefer|rangeValCopy|commentedOutCode)" linters: - gocritic # Package def is designed to contain global constants. - path: internal/def/ linters: - gochecknoglobals # Commands are allowed to contain a lot of flags. - path: main.go text: Function '(init|main)' linters: - funlen # Exclude known linters from partially hard-vendored code, # which is impossible to exclude via "nolint" comments. # - path: internal/hmac/ # text: "weak cryptographic primitive" # linters: # - gosec # Exclude some staticcheck messages # - linters: # - staticcheck # text: "SA9003:" # Exclude lll issues for long lines with go:generate # - linters: # - lll # source: "^//go:generate " # Independently from option `exclude` we use default exclude patterns, # it can be disabled by this option. To list all # excluded by default patterns execute `golangci-lint run --help`. # Default value for this option is true. exclude-use-default: false # Maximum issues count per one linter. Set to 0 to disable. Default is 50. max-issues-per-linter: 0 # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. max-same-issues: 0 # Show only new issues: if there are unstaged changes or untracked files, # only those changes are analyzed, else only changes in HEAD~ are analyzed. # It's a super-useful option for integration of golangci-lint into existing # large codebase. It's not practical to fix all existing issues at the moment # of integration: much better don't allow issues in new code. # Default is false. # new: false # Show only new issues created after git revision `REV` # new-from-rev: REV # Show only new issues created in git patch with set file path. # new-from-patch: path/to/patch/file check-1.2.1/LICENSE000066400000000000000000000020531356066224300136040ustar00rootroot00000000000000MIT License Copyright (c) 2017 Alex Efros 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. check-1.2.1/README.md000066400000000000000000000046261356066224300140660ustar00rootroot00000000000000# check [![GoDoc](https://godoc.org/github.com/powerman/check?status.svg)](http://godoc.org/github.com/powerman/check) [![Go Report Card](https://goreportcard.com/badge/github.com/powerman/check)](https://goreportcard.com/report/github.com/powerman/check) [![CircleCI](https://circleci.com/gh/powerman/check.svg?style=svg)](https://circleci.com/gh/powerman/check) [![Coverage Status](https://coveralls.io/repos/github/powerman/check/badge.svg?branch=master)](https://coveralls.io/github/powerman/check?branch=master) Helpers to complement Go [testing](https://golang.org/pkg/testing/) package. Write tests with ease and fun! This package is like [testify/assert](https://godoc.org/github.com/test-go/testify/assert) on steroids. :) ## Features - Compelling output from failed tests: - Very easy-to-read dumps for expected and actual values. - Same text diff you loved in testify/assert. - Also visual diff in [GoConvey](http://goconvey.co/) web UI, if you use it (recommended). - Statistics with amount of passed/failed checks. - Colored output in terminal. - 100% compatible with testing package - check package just provide convenient wrappers for `*testing.T` methods and doesn't introduce new concepts like BDD, custom test suite or unusual execution flow. - All checks you may ever need! :) - Very easy to add your own check functions. - Concise, handy and consistent API, without dot-import! ## Quickstart Just wrap each (including subtests) `*testing.T` using `check.T()` and write tests as usually with testing package. Call new methods provided by this package to have more clean/concise test code and cool dump/diff. ```go import "github.com/powerman/check" func TestSomething(tt *testing.T) { t := check.T(tt) t.Equal(2, 2) t.Log("You can use new t just like usual *testing.T") t.Run("Subtests/Parallel example", func(tt *testing.T) { t := check.T(tt) t.Parallel() t.NotEqual(2, 3, "should not be 3!") obj, err := NewObj() if t.Nil(err) { t.Match(obj.field, `^\d+$`) } }) } ``` To get optional statistics about executed checkers add: ```go func TestMain(m *testing.M) { check.TestMain(m) } ``` When use goconvey tool, to get nice diff in web UI [add](https://github.com/smartystreets/goconvey/issues/513): ```go import _ "github.com/smartystreets/goconvey/convey" ``` ## Installation Require [Go 1.9](https://golang.org/doc/go1.9#test-helper). ``` go get github.com/powerman/check ``` check-1.2.1/check.go000066400000000000000000001113461356066224300142110ustar00rootroot00000000000000package check import ( "bytes" "encoding/json" "fmt" "math" "reflect" "regexp" "strings" "testing" "time" pkgerrors "github.com/pkg/errors" ) var ( typString = reflect.TypeOf("") typBytes = reflect.TypeOf([]byte(nil)) typFloat64 = reflect.TypeOf(0.0) ) // C wraps *testing.T to make it convenient to call checkers in test. type C struct { *testing.T todo bool } const ( nameActual = "Actual" nameExpected = "Expected" ) // Parallel implements an internal workaround which have no visible // effect, so you should just call t.Parallel() as you usually do - it // will work as expected. func (t *C) Parallel() { t.Helper() // Goconvey anyway doesn't provide -test.cpu= and mixed output of // parallel tests result in reporting failed tests at wrong places // and with wrong failed tests count in web UI. if !flags.detect().conveyJSON { t.T.Parallel() } } // T create and return new *C, which wraps given tt and supposed to be // used inplace of it, providing you with access to many useful helpers in // addition to standard methods of *testing.T. // // It's convenient to rename Test function's arg from t to something // else, create wrapped variable with usual name t and use only t: // // func TestSomething(tt *testing.T) { // t := check.T(tt) // // use only t in test and don't touch tt anymore // } func T(tt *testing.T) *C { return &C{T: tt} } // TODO create and return new *C, which have only one difference from // original one: every passing check is now handled as failed and vice // versa (this doesn't affect boolean value returned by check). // You can continue using both old and new *C at same time. // // Swapping passed/failed gives you ability to temporary mark some failed // test as passed. For example, this may be useful to avoid broken builds // in CI. This is often better than commenting, deleting or skipping // broken test because it will continue to execute, and eventually when // reason why it fails will be fixed this test will became failed again - // notifying you the mark can and should be removed from this test now. // // func TestSomething(tt *testing.T) { // t := check.T(tt) // // Normal tests. // t.True(true) // // If you need to mark just one/few broken tests: // t.TODO().True(false) // t.True(true) // // If there are several broken tests mixed with working ones: // todo := t.TODO() // t.True(true) // todo.True(false) // t.True(true) // if todo.True(false) { // panic("never here") // } // // If all tests below this point are broken: // t = t.TODO() // t.True(false) // ... // } func (t *C) TODO() *C { return &C{T: t.T, todo: true} } func (t *C) pass() { statsMu.Lock() defer statsMu.Unlock() if stats[t.T] == nil { stats[t.T] = newTestStat(t.Name(), false) } if t.todo { stats[t.T].forged.value++ } else { stats[t.T].passed.value++ } } func (t *C) fail() { statsMu.Lock() defer statsMu.Unlock() if stats[t.T] == nil { stats[t.T] = newTestStat(t.Name(), false) } stats[t.T].failed.value++ } func (t *C) report(ok bool, msg []interface{}, checker string, name []string, args []interface{}) bool { t.Helper() if ok != t.todo { t.pass() return ok } if t.todo { checker = "TODO " + checker } dump := make([]dump, 0, len(args)) for _, arg := range args { dump = append(dump, newDump(arg)) } failure := new(bytes.Buffer) fmt.Fprintf(failure, "%s\nChecker: %s%s%s\n", format(msg...), ansiYellow, checker, ansiReset, ) failureShort := failure.String() // Reverse order to show Actual: last. for i := len(dump) - 1; i >= 0; i-- { fmt.Fprintf(failure, "%-10s", name[i]+":") switch name[i] { case nameActual: fmt.Fprint(failure, ansiRed) default: fmt.Fprint(failure, ansiGreen) } fmt.Fprintf(failure, "%s%s", dump[i], ansiReset) } failureLong := failure.String() wantDiff := len(dump) == 2 && name[0] == nameActual && name[1] == nameExpected if wantDiff { if reportToGoConvey(dump[0].String(), dump[1].String(), failureShort) == nil { t.Fail() } else { fmt.Fprintf(failure, "\n%s", colouredDiff(dump[0].diff(dump[1]))) t.Errorf("%s\n", failure) } } else { if reportToGoConvey("", "", failureLong) == nil { t.Fail() } else { t.Errorf("%s\n", failure) } } t.fail() return ok } func (t *C) reportShould1(funcName string, actual interface{}, msg []interface{}, ok bool) bool { t.Helper() return t.report(ok, msg, "Should "+funcName, []string{nameActual}, []interface{}{actual}) } func (t *C) reportShould2(funcName string, actual, expected interface{}, msg []interface{}, ok bool) bool { t.Helper() return t.report(ok, msg, "Should "+funcName, []string{nameActual, nameExpected}, []interface{}{actual, expected}) } func (t *C) report0(msg []interface{}, ok bool) bool { t.Helper() return t.report(ok, msg, callerFuncName(1), []string{}, []interface{}{}) } func (t *C) report1(actual interface{}, msg []interface{}, ok bool) bool { t.Helper() return t.report(ok, msg, callerFuncName(1), []string{nameActual}, []interface{}{actual}) } func (t *C) report2(actual, expected interface{}, msg []interface{}, ok bool) bool { t.Helper() checker, arg2Name := callerFuncName(1), nameExpected if strings.Contains(checker, "Match") { arg2Name = "Regex" } return t.report(ok, msg, checker, []string{nameActual, arg2Name}, []interface{}{actual, expected}) } func (t *C) report3(actual, expected1, expected2 interface{}, msg []interface{}, ok bool) bool { t.Helper() checker, arg2Name, arg3Name := callerFuncName(1), "arg1", "arg2" switch { case strings.Contains(checker, "Between"): arg2Name, arg3Name = "Min", "Max" case strings.Contains(checker, "Delta"): arg2Name, arg3Name = nameExpected, "Delta" case strings.Contains(checker, "SMAPE"): arg2Name, arg3Name = nameExpected, "SMAPE" } return t.report(ok, msg, checker, []string{nameActual, arg2Name, arg3Name}, []interface{}{actual, expected1, expected2}) } // Must interrupt test using t.FailNow if called with false value. // // This provides easy way to turn any check into assertion: // // t.Must(t.Nil(err)) func (t *C) Must(continueTest bool, msg ...interface{}) { t.Helper() t.report0(msg, continueTest) if !continueTest { t.FailNow() } } type ( // ShouldFunc1 is like Nil or Zero. ShouldFunc1 func(t *C, actual interface{}) bool // ShouldFunc2 is like Equal or Match. ShouldFunc2 func(t *C, actual, expected interface{}) bool ) // Should use user-provided check function to do actual check. // // anyShouldFunc must have type ShouldFunc1 or ShouldFunc2. It should // return true if check was successful. There is no need to call t.Error // in anyShouldFunc - this will be done automatically when it returns. // // args must contain at least 1 element for ShouldFunc1 and at least // 2 elements for ShouldFunc2. // Rest of elements will be processed as usual msg ...interface{} param. // // Example: // // func bePositive(_ *check.C, actual interface{}) bool { // return actual.(int) > 0 // } // func TestCustomCheck(tt *testing.T) { // t := check.T(tt) // t.Should(bePositive, 42, "custom check!!!") // } func (t *C) Should(anyShouldFunc interface{}, args ...interface{}) bool { t.Helper() switch f := anyShouldFunc.(type) { case func(t *C, actual interface{}) bool: return t.should1(f, args...) case func(t *C, actual, expected interface{}) bool: return t.should2(f, args...) default: panic("anyShouldFunc is not a ShouldFunc1 or ShouldFunc2") } } func (t *C) should1(f ShouldFunc1, args ...interface{}) bool { t.Helper() if len(args) < 1 { panic("not enough params for " + funcName(f)) } actual, msg := args[0], args[1:] return t.reportShould1(funcName(f), actual, msg, f(t, actual)) } func (t *C) should2(f ShouldFunc2, args ...interface{}) bool { t.Helper() if len(args) < 2 { panic("not enough params for " + funcName(f)) } actual, expected, msg := args[0], args[1], args[2:] return t.reportShould2(funcName(f), actual, expected, msg, f(t, actual, expected)) } // Nil checks for actual == nil. // // There is one subtle difference between this check and Go `== nil` (if // this surprises you then you should read // https://golang.org/doc/faq#nil_error first): // // var intPtr *int // var empty interface{} // var notEmpty interface{} = intPtr // t.True(intPtr == nil) // TRUE // t.True(empty == nil) // TRUE // t.True(notEmpty == nil) // FALSE // // When you call this function your actual value will be stored in // interface{} argument, and this makes any typed nil pointer value `!= // nil` inside this function (just like in example above happens with // notEmpty variable). // // As it is very common case to check some typed pointer using Nil this // check has to work around and detect nil even if usual `== nil` return // false. But this has nasty side effect: if actual value already was of // interface type and contains some typed nil pointer (which is usually // bad thing and should be avoid) then Nil check will pass (which may be // not what you want/expect): // // t.Nil(nil) // TRUE // t.Nil(intPtr) // TRUE // t.Nil(empty) // TRUE // t.Nil(notEmpty) // WARNING: also TRUE! // // Second subtle case is less usual: uintptr(0) is sorta nil, but not // really, so Nil(uintptr(0)) will fail. Nil(unsafe.Pointer(nil)) will // also fail, for the same reason. Please do not use this and consider // this behaviour undefined, because it may change in the future. func (t *C) Nil(actual interface{}, msg ...interface{}) bool { t.Helper() return t.report1(actual, msg, isNil(actual)) } func isNil(actual interface{}) bool { switch val := reflect.ValueOf(actual); val.Kind() { case reflect.Invalid: return actual == nil case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice: return val.IsNil() } return false } // NotNil checks for actual != nil. // // See Nil about subtle case in check logic. func (t *C) NotNil(actual interface{}, msg ...interface{}) bool { t.Helper() return t.report0(msg, !isNil(actual)) } // True checks for cond == true. // // This can be useful to use your own custom checks, but this way you // won't get nice dump/diff for actual/expected values. You'll still have // statistics about passed/failed checks and it's shorter than usual: // // if !cond { // t.Errorf(msg...) // } func (t *C) True(cond bool, msg ...interface{}) bool { t.Helper() return t.report0(msg, cond) } // False checks for cond == false. func (t *C) False(cond bool, msg ...interface{}) bool { t.Helper() return t.report0(msg, !cond) } // Equal checks for actual == expected. // // Note: For time.Time it uses actual.Equal(expected) instead. func (t *C) Equal(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, isEqual(actual, expected)) } func isEqual(actual, expected interface{}) bool { switch actual := actual.(type) { case time.Time: return actual.Equal(expected.(time.Time)) default: return actual == expected } } // EQ is a synonym for Equal. func (t *C) EQ(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.Equal(actual, expected, msg...) } // NotEqual checks for actual != expected. func (t *C) NotEqual(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, !isEqual(actual, expected)) } // NE is a synonym for NotEqual. func (t *C) NE(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.NotEqual(actual, expected, msg...) } // BytesEqual checks for bytes.Equal(actual, expected). // // Hint: BytesEqual([]byte{}, []byte(nil)) is true (unlike DeepEqual). func (t *C) BytesEqual(actual, expected []byte, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, bytes.Equal(actual, expected)) } // NotBytesEqual checks for !bytes.Equal(actual, expected). // // Hint: NotBytesEqual([]byte{}, []byte(nil)) is false (unlike NotDeepEqual). func (t *C) NotBytesEqual(actual, expected []byte, msg ...interface{}) bool { t.Helper() return t.report1(actual, msg, !bytes.Equal(actual, expected)) } // DeepEqual checks for reflect.DeepEqual(actual, expected). func (t *C) DeepEqual(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, reflect.DeepEqual(actual, expected)) } // NotDeepEqual checks for !reflect.DeepEqual(actual, expected). func (t *C) NotDeepEqual(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report1(actual, msg, !reflect.DeepEqual(actual, expected)) } // Match checks for regex.MatchString(actual). // // Regex type can be either *regexp.Regexp or string. // // Actual type can be: // - string - will match with actual // - []byte - will match with string(actual) // - []rune - will match with string(actual) // - fmt.Stringer - will match with actual.String() // - error - will match with actual.Error() // - nil - will not match (even with empty regex) func (t *C) Match(actual, regex interface{}, msg ...interface{}) bool { t.Helper() ok := isMatch(&actual, regex) return t.report2(actual, regex, msg, ok) } // isMatch updates actual to be a real string used for matching, to make // dump easier to understand, but this result in losing type information. func isMatch(actual *interface{}, regex interface{}) bool { if *actual == nil { return false } if !stringify(actual) { panic("actual is not a string, []byte, []rune, fmt.Stringer, error or nil") } s := (*actual).(string) switch v := regex.(type) { case *regexp.Regexp: return v.MatchString(s) case string: return regexp.MustCompile(v).MatchString(s) } panic("regex is not a *regexp.Regexp or string") } func stringify(arg *interface{}) bool { switch v := (*arg).(type) { case nil: return false case error: *arg = v.Error() case fmt.Stringer: *arg = v.String() default: typ := reflect.TypeOf(*arg) switch typ.Kind() { case reflect.String: case reflect.Slice: switch typ.Elem().Kind() { case reflect.Uint8, reflect.Int32: default: return false } default: return false } *arg = reflect.ValueOf(*arg).Convert(typString).Interface() } return true } // NotMatch checks for !regex.MatchString(actual). // // See Match about supported actual/regex types and check logic. func (t *C) NotMatch(actual, regex interface{}, msg ...interface{}) bool { t.Helper() ok := !isMatch(&actual, regex) return t.report2(actual, regex, msg, ok) } // Contains checks is actual contains substring/element expected. // // Element of array/slice/map is checked using == expected. // // Type of expected depends on type of actual: // - if actual is a string, then expected should be a string // - if actual is an array, then expected should have array's element type // - if actual is a slice, then expected should have slice's element type // - if actual is a map, then expected should have map's value type // // Hint: In a map it looks for a value, if you need to look for a key - // use HasKey instead. func (t *C) Contains(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, isContains(actual, expected)) } func isContains(actual, expected interface{}) (found bool) { switch valActual := reflect.ValueOf(actual); valActual.Kind() { case reflect.String: strActual := valActual.Convert(typString).Interface().(string) valExpected := reflect.ValueOf(expected) if valExpected.Kind() != reflect.String { panic("expected underlying type is not a string") } strExpected := valExpected.Convert(typString).Interface().(string) found = strings.Contains(strActual, strExpected) case reflect.Map: if valActual.Type().Elem() != reflect.TypeOf(expected) { panic("expected type not match actual element type") } keys := valActual.MapKeys() for i := 0; i < len(keys) && !found; i++ { found = valActual.MapIndex(keys[i]).Interface() == expected } case reflect.Slice, reflect.Array: if valActual.Type().Elem() != reflect.TypeOf(expected) { panic("expected type not match actual element type") } for i := 0; i < valActual.Len() && !found; i++ { found = valActual.Index(i).Interface() == expected } default: panic("actual is not a string, array, slice or map") } return found } // NotContains checks is actual not contains substring/element expected. // // See Contains about supported actual/expected types and check logic. func (t *C) NotContains(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, !isContains(actual, expected)) } // HasKey checks is actual has key expected. func (t *C) HasKey(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, hasKey(actual, expected)) } func hasKey(actual, expected interface{}) bool { return reflect.ValueOf(actual).MapIndex(reflect.ValueOf(expected)).IsValid() } // NotHasKey checks is actual has no key expected. func (t *C) NotHasKey(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, !hasKey(actual, expected)) } // Zero checks is actual is zero value of it's type. func (t *C) Zero(actual interface{}, msg ...interface{}) bool { t.Helper() return t.report1(actual, msg, isZero(actual)) } func isZero(actual interface{}) bool { if isNil(actual) { return true } else if typ := reflect.TypeOf(actual); typ.Comparable() { // Not Func, Map, Slice, Array with non-comparable // elements, Struct with non-comparable fields. return actual == reflect.Zero(typ).Interface() } else if typ.Kind() == reflect.Array { zero := true val := reflect.ValueOf(actual) for i := 0; i < val.Len() && zero; i++ { zero = isZero(val.Index(i).Interface()) } return zero } // Func, Struct with non-comparable fields. // Non-nil Map, Slice. return false } // NotZero checks is actual is not zero value of it's type. func (t *C) NotZero(actual interface{}, msg ...interface{}) bool { t.Helper() return t.report1(actual, msg, !isZero(actual)) } // Len checks is len(actual) == expected. func (t *C) Len(actual interface{}, expected int, msg ...interface{}) bool { t.Helper() l := reflect.ValueOf(actual).Len() return t.report2(l, expected, msg, l == expected) } // NotLen checks is len(actual) != expected. func (t *C) NotLen(actual interface{}, expected int, msg ...interface{}) bool { t.Helper() l := reflect.ValueOf(actual).Len() return t.report2(l, expected, msg, l != expected) } // Err checks is actual error is the same as expected error. // // It tries to recursively unwrap actual before checking using // errors.Unwrap() and github.com/pkg/errors.Cause(). // // They may be a different instances, but must have same type and value. // // Checking for nil is okay, but using Nil(actual) instead is more clean. func (t *C) Err(actual, expected error, msg ...interface{}) bool { t.Helper() actual = unwrapErr(actual) return t.report2(actual, expected, msg, fmt.Sprintf("%#v", actual) == fmt.Sprintf("%#v", expected)) } func unwrapErr(err error) (actual error) { defer func() { _ = recover() }() actual = err for { actual = pkgerrors.Cause(actual) wrapped, ok := actual.(interface{ Unwrap() error }) if !ok { break } unwrapped := wrapped.Unwrap() if unwrapped == nil { break } actual = unwrapped } return actual } // NotErr checks is actual error is not the same as expected error. // // It tries to recursively unwrap actual before checking using // errors.Unwrap() and github.com/pkg/errors.Cause(). // // They must have either different types or values (or one should be nil). // Different instances with same type and value will be considered the // same error, and so is both nil. func (t *C) NotErr(actual, expected error, msg ...interface{}) bool { t.Helper() actual = unwrapErr(actual) return t.report1(actual, msg, fmt.Sprintf("%#v", actual) != fmt.Sprintf("%#v", expected)) } // Panic checks is actual() panics. // // It is able to detect panic(nil)… but you should try to avoid using this. func (t *C) Panic(actual func(), msg ...interface{}) bool { t.Helper() var didPanic = true func() { defer func() { _ = recover() }() actual() didPanic = false }() return t.report0(msg, didPanic) } // NotPanic checks is actual() don't panics. // // It is able to detect panic(nil)… but you should try to avoid using this. func (t *C) NotPanic(actual func(), msg ...interface{}) bool { t.Helper() var didPanic = true func() { defer func() { _ = recover() }() actual() didPanic = false }() return t.report0(msg, !didPanic) } // PanicMatch checks is actual() panics and panic text match regex. // // Regex type can be either *regexp.Regexp or string. // // In case of panic(nil) it will match like panic(""). func (t *C) PanicMatch(actual func(), regex interface{}, msg ...interface{}) bool { t.Helper() var panicVal interface{} var didPanic = true func() { defer func() { panicVal = recover() }() actual() didPanic = false }() if !didPanic { return t.report0(msg, false) } switch panicVal.(type) { case string, error: default: panicVal = fmt.Sprintf("%#v", panicVal) } ok := isMatch(&panicVal, regex) return t.report2(panicVal, regex, msg, ok) } // PanicNotMatch checks is actual() panics and panic text not match regex. // // Regex type can be either *regexp.Regexp or string. // // In case of panic(nil) it will match like panic(""). func (t *C) PanicNotMatch(actual func(), regex interface{}, msg ...interface{}) bool { t.Helper() var panicVal interface{} var didPanic = true func() { defer func() { panicVal = recover() }() actual() didPanic = false }() if !didPanic { return t.report0(msg, false) } switch panicVal.(type) { case string, error: default: panicVal = fmt.Sprintf("%#v", panicVal) } ok := !isMatch(&panicVal, regex) return t.report2(panicVal, regex, msg, ok) } // Less checks for actual < expected. // // Both actual and expected must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) Less(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, isLess(actual, expected)) } func isLess(actual, expected interface{}) bool { switch v1, v2 := reflect.ValueOf(actual), reflect.ValueOf(expected); v1.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v1.Int() < v2.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v1.Uint() < v2.Uint() case reflect.Float32, reflect.Float64: return v1.Float() < v2.Float() case reflect.String: return v1.String() < v2.String() default: if actualTime, ok := actual.(time.Time); ok { return actualTime.Before(expected.(time.Time)) } } panic("actual is not a number, string or time.Time") } // LT is a synonym for Less. func (t *C) LT(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.Less(actual, expected, msg...) } // LessOrEqual checks for actual <= expected. // // Both actual and expected must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) LessOrEqual(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, !isGreater(actual, expected)) } func isGreater(actual, expected interface{}) bool { switch v1, v2 := reflect.ValueOf(actual), reflect.ValueOf(expected); v1.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v1.Int() > v2.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v1.Uint() > v2.Uint() case reflect.Float32, reflect.Float64: return v1.Float() > v2.Float() case reflect.String: return v1.String() > v2.String() default: if actualTime, ok := actual.(time.Time); ok { return actualTime.After(expected.(time.Time)) } } panic("actual is not a number, string or time.Time") } // LE is a synonym for LessOrEqual. func (t *C) LE(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.LessOrEqual(actual, expected, msg...) } // Greater checks for actual > expected. // // Both actual and expected must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) Greater(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, isGreater(actual, expected)) } // GT is a synonym for Greater. func (t *C) GT(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.Greater(actual, expected, msg...) } // GreaterOrEqual checks for actual >= expected. // // Both actual and expected must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) GreaterOrEqual(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, !isLess(actual, expected)) } // GE is a synonym for GreaterOrEqual. func (t *C) GE(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.GreaterOrEqual(actual, expected, msg...) } // Between checks for min < actual < max. // // All three actual, min and max must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) Between(actual, min, max interface{}, msg ...interface{}) bool { t.Helper() return t.report3(actual, min, max, msg, isBetween(actual, min, max)) } func isBetween(actual, min, max interface{}) bool { switch v, vmin, vmax := reflect.ValueOf(actual), reflect.ValueOf(min), reflect.ValueOf(max); v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return vmin.Int() < v.Int() && v.Int() < vmax.Int() case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return vmin.Uint() < v.Uint() && v.Uint() < vmax.Uint() case reflect.Float32, reflect.Float64: return vmin.Float() < v.Float() && v.Float() < vmax.Float() case reflect.String: return vmin.String() < v.String() && v.String() < vmax.String() default: if actualTime, ok := actual.(time.Time); ok { minTime, maxTime := min.(time.Time), max.(time.Time) return minTime.Before(actualTime) && actualTime.Before(maxTime) } } panic("actual is not a number, string or time.Time") } // NotBetween checks for actual <= min or max <= actual. // // All three actual, min and max must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) NotBetween(actual, min, max interface{}, msg ...interface{}) bool { t.Helper() return t.report3(actual, min, max, msg, !isBetween(actual, min, max)) } // BetweenOrEqual checks for min <= actual <= max. // // All three actual, min and max must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) BetweenOrEqual(actual, min, max interface{}, msg ...interface{}) bool { t.Helper() return t.report3(actual, min, max, msg, isBetween(actual, min, max) || isEqual(actual, min) || isEqual(actual, max)) } // NotBetweenOrEqual checks for actual < min or max < actual. // // All three actual, min and max must be either: // - signed integers // - unsigned integers // - floats // - strings // - time.Time func (t *C) NotBetweenOrEqual(actual, min, max interface{}, msg ...interface{}) bool { t.Helper() return t.report3(actual, min, max, msg, !(isBetween(actual, min, max) || isEqual(actual, min) || isEqual(actual, max))) } // InDelta checks for expected-delta <= actual <= expected+delta. // // All three actual, expected and delta must be either: // - signed integers // - unsigned integers // - floats // - time.Time (in this case delta must be time.Duration) func (t *C) InDelta(actual, expected, delta interface{}, msg ...interface{}) bool { t.Helper() return t.report3(actual, expected, delta, msg, isInDelta(actual, expected, delta)) } func isInDelta(actual, expected, delta interface{}) bool { switch v, e, d := reflect.ValueOf(actual), reflect.ValueOf(expected), reflect.ValueOf(delta); v.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: min, max := e.Int()-d.Int(), e.Int()+d.Int() return min <= v.Int() && v.Int() <= max case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: min, max := e.Uint()-d.Uint(), e.Uint()+d.Uint() return min <= v.Uint() && v.Uint() <= max case reflect.Float32, reflect.Float64: min, max := e.Float()-d.Float(), e.Float()+d.Float() return min <= v.Float() && v.Float() <= max default: if actualTime, ok := actual.(time.Time); ok { expectedTime, dur := expected.(time.Time), delta.(time.Duration) minTime, maxTime := expectedTime.Add(-dur), expectedTime.Add(dur) return minTime.Before(actualTime) && actualTime.Before(maxTime) || actualTime.Equal(minTime) || actualTime.Equal(maxTime) } } panic("actual is not a number or time.Time") } // NotInDelta checks for actual < expected-delta or expected+delta < actual. // // All three actual, expected and delta must be either: // - signed integers // - unsigned integers // - floats // - time.Time (in this case delta must be time.Duration) func (t *C) NotInDelta(actual, expected, delta interface{}, msg ...interface{}) bool { t.Helper() return t.report3(actual, expected, delta, msg, !isInDelta(actual, expected, delta)) } // InSMAPE checks that actual and expected have a symmetric mean absolute // percentage error (SMAPE) is less than given smape. // // Both actual and expected must be either: // - signed integers // - unsigned integers // - floats // // Allowed smape values are: 0.0 < smape < 100.0. // // Used formula returns SMAPE value between 0 and 100 (percents): // - 0.0 when actual == expected // - ~0.5 when they differs in ~1% // - ~5 when they differs in ~10% // - ~20 when they differs in 1.5 times // - ~33 when they differs in 2 times // - 50.0 when they differs in 3 times // - ~82 when they differs in 10 times // - 99.0+ when actual and expected differs in 200+ times // - 100.0 when only one of actual or expected is 0 or one of them is // positive while another is negative func (t *C) InSMAPE(actual, expected interface{}, smape float64, msg ...interface{}) bool { t.Helper() return t.report3(actual, expected, smape, msg, isInSMAPE(actual, expected, smape)) } func isInSMAPE(actual, expected interface{}, smape float64) bool { if !(0 < smape && smape < 100) { panic("smape is not in allowed range: 0 < smape < 100") } a := reflect.ValueOf(actual).Convert(typFloat64).Float() e := reflect.ValueOf(expected).Convert(typFloat64).Float() if a == 0 && e == 0 { return true // avoid division by zero in legal use case } return 100*math.Abs(e-a)/(math.Abs(e)+math.Abs(a)) < smape } // NotInSMAPE checks that actual and expected have a symmetric mean // absolute percentage error (SMAPE) is greater than or equal to given // smape. // // See InSMAPE about supported actual/expected types and check logic. func (t *C) NotInSMAPE(actual, expected interface{}, smape float64, msg ...interface{}) bool { t.Helper() return t.report3(actual, expected, smape, msg, !isInSMAPE(actual, expected, smape)) } // HasPrefix checks for strings.HasPrefix(actual, expected). // // Both actual and expected may have any of these types: // - string - will use as is // - []byte - will convert with string() // - []rune - will convert with string() // - fmt.Stringer - will convert with actual.String() // - error - will convert with actual.Error() // - nil - check will always fail func (t *C) HasPrefix(actual, expected interface{}, msg ...interface{}) bool { t.Helper() ok := isHasPrefix(&actual, &expected) return t.report2(actual, expected, msg, ok) } // isHasPrefix updates actual and expected to be a real string used for check, // to make dump easier to understand, but this result in losing type information. func isHasPrefix(actual, expected *interface{}) bool { if *actual == nil || *expected == nil { return false } if !stringify(actual) { panic("actual is not a string, []byte, []rune, fmt.Stringer, error or nil") } if !stringify(expected) { panic("expected is not a string, []byte, []rune, fmt.Stringer, error or nil") } return strings.HasPrefix((*actual).(string), (*expected).(string)) } // NotHasPrefix checks for !strings.HasPrefix(actual, expected). // // See HasPrefix about supported actual/expected types and check logic. func (t *C) NotHasPrefix(actual, expected interface{}, msg ...interface{}) bool { t.Helper() ok := !isHasPrefix(&actual, &expected) return t.report2(actual, expected, msg, ok) } // HasSuffix checks for strings.HasSuffix(actual, expected). // // Both actual and expected may have any of these types: // - string - will use as is // - []byte - will convert with string() // - []rune - will convert with string() // - fmt.Stringer - will convert with actual.String() // - error - will convert with actual.Error() // - nil - check will always fail func (t *C) HasSuffix(actual, expected interface{}, msg ...interface{}) bool { t.Helper() ok := isHasSuffix(&actual, &expected) return t.report2(actual, expected, msg, ok) } // isHasSuffix updates actual and expected to be a real string used for check, // to make dump easier to understand, but this result in losing type information. func isHasSuffix(actual, expected *interface{}) bool { if *actual == nil || *expected == nil { return false } if !stringify(actual) { panic("actual is not a string, []byte, []rune, fmt.Stringer, error or nil") } if !stringify(expected) { panic("expected is not a string, []byte, []rune, fmt.Stringer, error or nil") } return strings.HasSuffix((*actual).(string), (*expected).(string)) } // NotHasSuffix checks for !strings.HasSuffix(actual, expected). // // See HasSuffix about supported actual/expected types and check logic. func (t *C) NotHasSuffix(actual, expected interface{}, msg ...interface{}) bool { t.Helper() ok := !isHasSuffix(&actual, &expected) return t.report2(actual, expected, msg, ok) } // JSONEqual normalize formatting of actual and expected (if they're valid // JSON) and then checks for bytes.Equal(actual, expected). // // Both actual and expected may have any of these types: // - string // - []byte // - json.RawMessage // - *json.RawMessage // - nil // // In case any of actual or expected is nil or empty or (for string or // []byte) is invalid JSON - check will fail. func (t *C) JSONEqual(actual, expected interface{}, msg ...interface{}) bool { t.Helper() ok := isJSONEqual(actual, expected) if !ok { if buf := jsonify(actual); len(buf) != 0 { actual = buf } if buf := jsonify(expected); len(buf) != 0 { expected = buf } } return t.report2(actual, expected, msg, ok) } func isJSONEqual(actual, expected interface{}) bool { jsonActual, jsonExpected := jsonify(actual), jsonify(expected) return len(jsonActual) != 0 && len(jsonExpected) != 0 && bytes.Equal(jsonActual, jsonExpected) } func jsonify(arg interface{}) json.RawMessage { switch v := (arg).(type) { case nil: return nil case json.RawMessage: return v case *json.RawMessage: if v == nil { return nil } return *v } buf := reflect.ValueOf(arg).Convert(typBytes).Interface().([]byte) var v interface{} err := json.Unmarshal(buf, &v) if err != nil { return nil } buf, err = json.Marshal(v) if err != nil { return nil } return buf } // HasType checks is actual has same type as expected. func (t *C) HasType(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, reflect.TypeOf(actual) == reflect.TypeOf(expected)) } // NotHasType checks is actual has not same type as expected. func (t *C) NotHasType(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, reflect.TypeOf(actual) != reflect.TypeOf(expected)) } // Implements checks is actual implements interface pointed by expected. // // You must use pointer to interface type in expected: // // t.Implements(os.Stdin, (*io.Reader)(nil)) func (t *C) Implements(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, isImplements(actual, expected)) } func isImplements(actual, expected interface{}) bool { typActual := reflect.TypeOf(actual) if typActual.Kind() != reflect.Ptr { typActual = reflect.PtrTo(typActual) } return typActual.Implements(reflect.TypeOf(expected).Elem()) } // NotImplements checks is actual does not implements interface pointed by expected. // // You must use pointer to interface type in expected: // // t.NotImplements(os.Stdin, (*fmt.Stringer)(nil)) func (t *C) NotImplements(actual, expected interface{}, msg ...interface{}) bool { t.Helper() return t.report2(actual, expected, msg, !isImplements(actual, expected)) } check-1.2.1/check_test.go000066400000000000000000001317361356066224300152550ustar00rootroot00000000000000package check_test import ( "encoding/json" "errors" "fmt" "io" "net" "os" "reflect" "regexp" "testing" "time" pkgerrors "github.com/pkg/errors" "github.com/powerman/check" ) func init() { time.Local = time.UTC } type ( myInt int myString string myStruct struct { i int s string } myError struct{ s string } ) func (e myError) Error() string { return e.s } var ( // Zero values for standard types. zBool bool zInt int zInt8 int8 zInt16 int16 zInt32 int32 zInt64 int64 zUint uint zUint8 uint8 zUint16 uint16 zUint32 uint32 zUint64 uint64 zUintptr uintptr zFloat32 float32 zFloat64 float64 zArray0 [0]int zArray1 [1]int zChan chan int zFunc func() zIface interface{} zMap map[int]int zSlice []int zString string zStruct struct{} // zUnsafe unsafe.Pointer // don't like to import unsafe zBoolPtr *bool zIntPtr *int zInt8Ptr *int8 zInt16Ptr *int16 zInt32Ptr *int32 zInt64Ptr *int64 zUintPtr *uint zUint8Ptr *uint8 zUint16Ptr *uint16 zUint32Ptr *uint32 zUint64Ptr *uint64 zUintptrPtr *uintptr zFloat32Ptr *float32 zFloat64Ptr *float64 zArray0Ptr *[0]int zArray1Ptr *[1]int zChanPtr *chan int zFuncPtr *func() zIfacePtr *interface{} zMapPtr *map[int]int zSlicePtr *[]int zStringPtr *string zStructPtr *struct{} // zUnsafePtr *unsafe.Pointer // don't like to import unsafe // Zero values for named types. zMyInt myInt zMyString myString zJSON json.RawMessage zJSONPtr *json.RawMessage zTime time.Time // Initialized but otherwise zero-like values. vChan = make(chan int) vFunc = func() {} vIface interface{} = zIntPtr vMap = make(map[int]int) vSlice = make([]int, 0) // Non-zero values. xBool = true xInt = -42 xInt8 int8 = -8 xInt16 int16 = -16 xInt32 int32 = -32 xInt64 int64 = -64 xUint uint = 42 xUint8 uint8 = 8 xUint16 uint16 = 16 xUint32 uint32 = 32 xUint64 uint64 = 64 xUintptr uintptr = 0xDEADBEEF xFloat32 float32 = -3.2 xFloat64 = 6.4 xArray1 = [1]int{-1} xChan = make(chan int, 1) xFunc = func() { panic(nil) } xIface io.Reader = os.Stdin xMap = map[int]int{2: -2, 3: -3, 5: -5} xSlice = []int{3, 5, 8} xString = "" xStruct = myStruct{i: 10, s: "ten"} // xUnsafe = unsafe.Pointer(&xUintptr) // don't like to import unsafe xBoolPtr = &xBool xIntPtr = &xInt xInt8Ptr = &xInt8 xInt16Ptr = &xInt16 xInt32Ptr = &xInt32 xInt64Ptr = &xInt64 xUintPtr = &xUint xUint8Ptr = &xUint8 xUint16Ptr = &xUint16 xUint32Ptr = &xUint32 xUint64Ptr = &xUint64 xUintptrPtr = &xUintptr xFloat32Ptr = &xFloat32 xFloat64Ptr = &xFloat64 xArray1Ptr = &xArray1 xChanPtr = &xChan xFuncPtr = &xFunc xIfacePtr = &xIface xMapPtr = &xMap xSlicePtr = &xSlice xStringPtr = &xString xStructPtr = &xStruct // xUnsafePtr *unsafe.Pointer = &xUnsafe // don't like to import unsafe xMyInt myInt = 31337 xMyString myString = "xyz" xJSON json.RawMessage = []byte(`{"s":"ten","i":10}`) xJSONPtr = &xJSON xTime = time.Now() xTimeEST = xTime.In(func() *time.Location { loc, _ := time.LoadLocation("EST"); return loc }()) ) func TestTODO(tt *testing.T) { t := check.T(tt) // Normal tests. t.True(true) // If you need to mark just one/few broken tests: t.TODO().True(false) t.True(true) // If there are several broken tests mixed with working ones: todo := t.TODO() t.True(true) todo.True(false) t.True(true) if todo.True(false) { panic("never here") } // If all tests below this point are broken: t = t.TODO() t.True(false) // Second TODO() doesn't switch it off: t = t.TODO() t.True(false) } func TestMust(tt *testing.T) { t := check.T(tt) t.Must(t.Nil(nil)) t.Must(t.NotNil(false)) } func bePositive(_ *check.C, actual interface{}) bool { return actual.(int) > 0 } func beEqual(_ *check.C, actual, expected interface{}) bool { return actual == expected } func TestCheckerShould(tt *testing.T) { t := check.T(tt) t.Should(bePositive, 42, "custom check!!!") t.Panic(func() { t.Should(bePositive, "42", "bad arg type") }) t.TODO().Should(func(_ *check.C, _ interface{}) bool { return false }, 42) t.Should(beEqual, 123, 123) t.TODO().Should(beEqual, 123, 124) t.Panic(func() { t.Should(func() {}, nil) }) t.Panic(func() { t.Should(bePositive) }) t.Panic(func() { t.Should(beEqual, nil) }) } func TestCheckerNilTrue(tt *testing.T) { t := check.T(tt) todo := t.TODO() // Ensure expected values t.Equal(zBool, false) // gometalinter hates zBool==false t.True(zInt == 0) t.True(zInt8 == 0) t.True(zInt16 == 0) t.True(zInt32 == 0) t.True(zInt64 == 0) t.True(zUint == 0) t.True(zUint8 == 0) t.True(zUint16 == 0) t.True(zUint32 == 0) t.True(zUint64 == 0) t.True(zUintptr == 0) t.True(zFloat32 == 0) t.True(zFloat64 == 0) t.True(zArray0 == [0]int{}) t.True(zArray1 == [1]int{}) t.True(zChan == nil) t.True(zFunc == nil) t.True(zIface == nil) t.True(zMap == nil) t.True(zSlice == nil) t.True(zString == "") t.True(zStruct == struct{}{}) // t.True(zUnsafe == nil) t.True(zBoolPtr == nil) t.True(zIntPtr == nil) t.True(zInt8Ptr == nil) t.True(zInt16Ptr == nil) t.True(zInt32Ptr == nil) t.True(zInt64Ptr == nil) t.True(zUintPtr == nil) t.True(zUint8Ptr == nil) t.True(zUint16Ptr == nil) t.True(zUint32Ptr == nil) t.True(zUint64Ptr == nil) t.True(zUintptrPtr == nil) t.True(zFloat32Ptr == nil) t.True(zFloat64Ptr == nil) t.True(zArray0Ptr == nil) t.True(zArray1Ptr == nil) t.True(zChanPtr == nil) t.True(zFuncPtr == nil) t.True(zIfacePtr == nil) t.True(zMapPtr == nil) t.True(zSlicePtr == nil) t.True(zStringPtr == nil) t.True(zStructPtr == nil) // t.True(zUnsafePtr == nil) t.True(zMyInt == 0) t.True(zMyString == "") t.True(zJSON == nil) t.True(zJSONPtr == nil) t.True(zTime == time.Time{}) t.False(vChan == nil) t.False(vFunc == nil) t.False(vIface == nil) t.False(vMap == nil) t.False(vSlice == nil) // Subtle case when t.Nil() differs from == nil. zIface = zIntPtr t.Nil(zIface) t.False(zIface == nil) zIface = nil t.Nil(zIface) t.True(zIface == nil) cases := []struct { equalNil bool isNil bool actual interface{} }{ {true, true, nil}, {false, false, zBool}, {false, false, zInt}, {false, false, zInt8}, {false, false, zInt16}, {false, false, zInt32}, {false, false, zInt64}, {false, false, zUint}, {false, false, zUint8}, {false, false, zUint16}, {false, false, zUint32}, {false, false, zUint64}, {false, false, zUintptr}, {false, false, zFloat32}, {false, false, zFloat64}, {false, false, zArray0}, {false, false, zArray1}, {false, true, zChan}, {false, true, zFunc}, {true, true, zIface}, {false, true, zMap}, {false, true, zSlice}, {false, false, zString}, {false, false, zStruct}, // {false, false, zUnsafe}, {false, true, zBoolPtr}, {false, true, zIntPtr}, {false, true, zInt8Ptr}, {false, true, zInt16Ptr}, {false, true, zInt32Ptr}, {false, true, zInt64Ptr}, {false, true, zUintPtr}, {false, true, zUint8Ptr}, {false, true, zUint16Ptr}, {false, true, zUint32Ptr}, {false, true, zUint64Ptr}, {false, true, zUintptrPtr}, {false, true, zFloat32Ptr}, {false, true, zFloat64Ptr}, {false, true, zArray0Ptr}, {false, true, zArray1Ptr}, {false, true, zChanPtr}, {false, true, zFuncPtr}, {false, true, zIfacePtr}, {false, true, zMapPtr}, {false, true, zSlicePtr}, {false, true, zStringPtr}, {false, true, zStructPtr}, // {false, true, zUnsafePtr}, {false, false, zMyInt}, {false, false, zMyString}, {false, true, zJSON}, {false, true, zJSONPtr}, {false, false, zTime}, {false, false, vChan}, {false, false, vFunc}, {false, true, vIface}, // WARNING false-positive (documented) {false, false, vMap}, {false, false, vSlice}, } for i, v := range cases { msg := fmt.Sprintf("case %d: %#v", i, v.actual) if v.equalNil { t.True(v.actual == nil, msg) } else { t.False(v.actual == nil, msg) } if v.isNil { t.Nil(v.actual, msg) todo.NotNil(v.actual, msg) } else { todo.Nil(v.actual, msg) t.NotNil(v.actual, msg) } } } func TestCheckerEqual(tt *testing.T) { t := check.T(tt) todo := t.TODO() cases := []struct { comparable bool actual interface{} actual2 interface{} }{ {true, zBool, xBool}, {true, zInt, xInt}, {true, zInt8, xInt8}, {true, zInt16, xInt16}, {true, zInt32, xInt32}, {true, zInt64, xInt64}, {true, zUint, xUint}, {true, zUint8, xUint8}, {true, zUint16, xUint16}, {true, zUint32, xUint32}, {true, zUint64, xUint64}, {true, zUintptr, xUintptr}, {true, zFloat32, xFloat32}, {true, zFloat64, xFloat64}, {true, zArray0, xArray1}, {true, zArray1, xArray1}, {true, zChan, xChan}, {false, zFunc, xFunc}, {true, zIface, xIface}, {false, zMap, xMap}, {false, zSlice, xSlice}, {true, zString, xString}, {true, zStruct, xStruct}, {true, zBoolPtr, xBoolPtr}, {true, zIntPtr, xIntPtr}, {true, zInt8Ptr, xInt8Ptr}, {true, zInt16Ptr, xInt16Ptr}, {true, zInt32Ptr, xInt32Ptr}, {true, zInt64Ptr, xInt64Ptr}, {true, zUintPtr, xUintPtr}, {true, zUint8Ptr, xUint8Ptr}, {true, zUint16Ptr, xUint16Ptr}, {true, zUint32Ptr, xUint32Ptr}, {true, zUint64Ptr, xUint64Ptr}, {true, zUintptrPtr, xUintptrPtr}, {true, zFloat32Ptr, xFloat32Ptr}, {true, zFloat64Ptr, xFloat64Ptr}, {true, zArray0Ptr, xArray1Ptr}, {true, zArray1Ptr, xArray1Ptr}, {true, zChanPtr, xChanPtr}, {true, zFuncPtr, xFuncPtr}, {true, zIfacePtr, xIfacePtr}, {true, zMapPtr, xMapPtr}, {true, zSlicePtr, xSlicePtr}, {true, zStringPtr, xStringPtr}, {true, zStructPtr, xStructPtr}, {true, zMyInt, xMyInt}, {true, zMyString, xMyString}, {false, zJSON, xJSON}, {true, zJSONPtr, xJSONPtr}, {true, zTime, xTime}, {true, vChan, xChan}, {false, vFunc, xFunc}, {true, vIface, xIface}, {false, vMap, xMap}, {false, vSlice, xSlice}, {true, "one\ntwo\nend", "one\nTWO\nend"}, {true, io.EOF, io.ErrUnexpectedEOF}, {true, t, tt}, {true, int64(42), int32(42)}, {false, []byte{}, []byte(nil)}, } for _, v := range cases { if v.comparable { t.Equal(v.actual, v.actual) t.EQ(v.actual, v.actual) t.DeepEqual(v.actual, v.actual) todo.NotEqual(v.actual, v.actual) todo.NE(v.actual, v.actual) todo.NotDeepEqual(v.actual, v.actual) t.Equal(v.actual2, v.actual2) t.EQ(v.actual2, v.actual2) t.DeepEqual(v.actual2, v.actual2) todo.NotEqual(v.actual2, v.actual2) todo.NE(v.actual2, v.actual2) todo.NotDeepEqual(v.actual2, v.actual2) todo.Equal(v.actual, v.actual2) todo.EQ(v.actual, v.actual2) todo.DeepEqual(v.actual, v.actual2) t.NotEqual(v.actual, v.actual2) t.NE(v.actual, v.actual2) t.NotDeepEqual(v.actual, v.actual2) } else { t.Panic(func() { t.Equal(v.actual, v.actual) }) t.Panic(func() { t.EQ(v.actual, v.actual) }) t.Panic(func() { t.NotEqual(v.actual, v.actual) }) t.Panic(func() { t.NE(v.actual, v.actual) }) if reflect.TypeOf(v.actual).Kind() != reflect.Func { t.DeepEqual(v.actual, v.actual) todo.NotDeepEqual(v.actual, v.actual) t.DeepEqual(v.actual2, v.actual2) todo.NotDeepEqual(v.actual2, v.actual2) todo.DeepEqual(v.actual, v.actual2) t.NotDeepEqual(v.actual, v.actual2) } } } // No alternative value for .actual2. t.Equal(nil, nil) t.EQ(nil, nil) t.DeepEqual(nil, nil) todo.NotEqual(nil, nil) todo.NE(nil, nil) todo.NotDeepEqual(nil, nil) // Equal match, DeepEqual not match. t.False(xTime == xTimeEST) t.Equal(xTime, xTimeEST) t.EQ(xTime, xTimeEST) t.NotDeepEqual(xTime, xTimeEST) todo.NotEqual(xTime, xTimeEST) todo.NE(xTime, xTimeEST) todo.DeepEqual(xTime, xTimeEST) // Equal not match or panic, DeepEqual match. type notComparable struct { s string is []int } cases = []struct { comparable bool actual interface{} actual2 interface{} }{ {true, io.EOF, errors.New("EOF")}, {true, &testing.T{}, &testing.T{}}, {false, []byte{2, 5}, []byte{2, 5}}, {false, notComparable{"a", []int{3, 5}}, notComparable{"a", []int{3, 5}}}, } for _, v := range cases { if v.comparable { t.False(v.actual == v.actual2) todo.Equal(v.actual, v.actual2) todo.EQ(v.actual, v.actual2) t.NotEqual(v.actual, v.actual2) t.NE(v.actual, v.actual2) } t.DeepEqual(v.actual, v.actual2) todo.NotDeepEqual(v.actual, v.actual2) } } func TestCheckerBytesEqual(tt *testing.T) { t := check.T(tt) todo := t.TODO() cases := []struct { equal bool actual []byte expected []byte }{ {true, nil, nil}, {true, []byte(nil), []byte(nil)}, {true, []byte{}, []byte{}}, {true, []byte(nil), nil}, {true, []byte{}, nil}, {true, []byte(nil), []byte{}}, {true, []byte{0}, []byte{0}}, {false, []byte{0}, nil}, {false, []byte{0}, []byte(nil)}, {false, []byte{0}, []byte{}}, {false, []byte{0}, []byte{0, 0}}, } for _, v := range cases { if v.equal { t.BytesEqual(v.actual, v.expected) todo.NotBytesEqual(v.actual, v.expected) } else { todo.BytesEqual(v.actual, v.expected) t.NotBytesEqual(v.actual, v.expected) } } } func TestCheckerMatch(tt *testing.T) { t := check.T(tt) todo := t.TODO() types := []struct { actual bool expected bool zero interface{} }{ {true, false, nil}, {false, false, zBool}, {false, false, zInt}, {false, false, zInt8}, {false, false, zInt16}, {false, false, zInt32}, {false, false, zInt64}, {false, false, zUint}, {false, false, zUint8}, {false, false, zUint16}, {false, false, zUint32}, {false, false, zUint64}, {false, false, zUintptr}, {false, false, zFloat32}, {false, false, zFloat64}, {false, false, zArray0}, {false, false, zArray1}, {false, false, zChan}, {false, false, zFunc}, {false, false, zIface}, {false, false, zMap}, {false, false, zSlice}, {true, true, zString}, {false, false, zStruct}, {false, false, zBoolPtr}, {false, false, zIntPtr}, {false, false, zInt8Ptr}, {false, false, zInt16Ptr}, {false, false, zInt32Ptr}, {false, false, zInt64Ptr}, {false, false, zUintPtr}, {false, false, zUint8Ptr}, {false, false, zUint16Ptr}, {false, false, zUint32Ptr}, {false, false, zUint64Ptr}, {false, false, zUintptrPtr}, {false, false, zFloat32Ptr}, {false, false, zFloat64Ptr}, {false, false, zArray0Ptr}, {false, false, zArray1Ptr}, {false, false, zChanPtr}, {false, false, zFuncPtr}, {false, false, zIfacePtr}, {false, false, zMapPtr}, {false, false, zSlicePtr}, {false, false, zStringPtr}, {false, false, zStructPtr}, {false, false, zMyInt}, {true, false, zMyString}, {true, false, zJSON}, {false, false, zJSONPtr}, {true, false, zTime}, {true, false, time.Sunday}, {true, false, errors.New("")}, {true, false, []byte(nil)}, {true, false, []rune(nil)}, {true, true, regexp.MustCompile("")}, // it's also a Stringer {false, false, (*regexp.Regexp)(nil)}, {false, false, regexp.Regexp{}}, } for i, va := range types { for j, ve := range types { msg := fmt.Sprintf("case %d/%d: %#v, %#v", i, j, va.zero, ve.zero) switch va.zero.(type) { case nil: todo.Match(va.zero, ve.zero, msg) default: if va.actual && ve.expected { t.Match(va.zero, ve.zero, msg) } else { t.Panic(func() { t.Match(va.zero, ve.zero) }, msg) } } } } cases := []struct { actual interface{} regexMatch interface{} regexNotMatch interface{} }{ {"", `^$`, `.`}, {myString("Test"), regexp.MustCompile(`st$`), regexp.MustCompile(`ST$`)}, {[]byte(nil), `^$`, `nil`}, {[]byte("Test"), regexp.MustCompile(`st$`), regexp.MustCompile(`ST$`)}, {[]rune(nil), `^$`, `nil`}, {[]rune("Test"), regexp.MustCompile(`st$`), regexp.MustCompile(`ST$`)}, {zTime, `00:00:00`, `01:01:01`}, {time.Sunday, regexp.MustCompile(`^Sun`), regexp.MustCompile(`Sun$`)}, {errors.New(""), `^$`, `nil`}, {io.EOF, regexp.MustCompile(`^EO`), regexp.MustCompile(`EO$`)}, } for _, v := range cases { t.Match(v.actual, v.regexMatch) todo.Match(v.actual, v.regexNotMatch) } // No value for .regexMatch. todo.Match(nil, ``) todo.Match(nil, regexp.MustCompile(``)) t.NotMatch(nil, ``) t.NotMatch(nil, regexp.MustCompile(``)) } func TestCheckerContains(tt *testing.T) { t := check.T(tt) failures := []struct { panic bool actual interface{} expected interface{} }{ {true, nil, nil}, {true, zBool, zBool}, {true, zInt, zInt}, {true, zInt8, zInt8}, {true, zInt16, zInt16}, {true, zInt32, zInt32}, {true, zInt64, zInt64}, {true, zUint, zUint}, {true, zUint8, zUint8}, {true, zUint16, zUint16}, {true, zUint32, zUint32}, {true, zUint64, zUint64}, {true, zUintptr, zUintptr}, {true, zFloat32, zFloat32}, {true, zFloat64, zFloat64}, {true, zArray0, zBool}, {false, zArray0, xInt}, {true, zArray1, zBool}, {false, zArray1, xInt}, {true, zChan, zChan}, {true, zFunc, zFunc}, {true, zIface, zIface}, {true, zMap, zBool}, {false, zMap, xInt}, {true, zSlice, zBool}, {false, zSlice, xInt}, {true, zString, zBool}, {false, zString, xString}, {true, zStruct, zStruct}, {true, zBoolPtr, zBoolPtr}, {true, zIntPtr, zIntPtr}, {true, zInt8Ptr, zInt8Ptr}, {true, zInt16Ptr, zInt16Ptr}, {true, zInt32Ptr, zInt32Ptr}, {true, zInt64Ptr, zInt64Ptr}, {true, zUintPtr, zUintPtr}, {true, zUint8Ptr, zUint8Ptr}, {true, zUint16Ptr, zUint16Ptr}, {true, zUint32Ptr, zUint32Ptr}, {true, zUint64Ptr, zUint64Ptr}, {true, zUintptrPtr, zUintptrPtr}, {true, zFloat32Ptr, zFloat32Ptr}, {true, zFloat64Ptr, zFloat64Ptr}, {true, zArray0Ptr, zArray0Ptr}, {true, zArray1Ptr, zArray1Ptr}, {true, zChanPtr, zChanPtr}, {true, zFuncPtr, zFuncPtr}, {true, zIfacePtr, zIfacePtr}, {true, zMapPtr, zMapPtr}, {true, zSlicePtr, zSlicePtr}, {true, zStringPtr, zStringPtr}, {true, zStructPtr, zStructPtr}, {true, zMyInt, zMyInt}, {true, zMyString, zBool}, {false, zMyString, xString}, {true, zJSON, zBool}, {false, zJSON, xUint8}, {true, zJSONPtr, zJSONPtr}, {true, zTime, zTime}, } for i, v := range failures { msg := fmt.Sprintf("case %d: %#v, %#v", i, v.actual, v.expected) if v.panic { t.Panic(func() { t.Contains(v.actual, v.expected) }, msg) } else { t.NotContains(v.actual, v.expected, msg) } } t.Contains("", "") t.Contains("Test", "") t.Contains(myString("Test"), "es") t.Contains([...]time.Time{zTime, xTime, xTimeEST}, xTime) t.Contains([]*time.Time{&zTime, &xTime, &xTimeEST}, &xTime) t.Contains([]byte("Test"), byte('e')) t.Contains([]rune("Test"), 'e') t.Contains(map[int]string{2: "two", 5: "five", 10: "ten"}, "five") t.Contains(map[string]int{"two": 2, "five": 5, "ten": 10}, 5) t.NotContains(map[string]int{"two": 2, "five": 5, "ten": 10}, 0) } func TestCheckerHasKey(tt *testing.T) { t := check.T(tt) failures := []struct { panic bool actual interface{} expected interface{} }{ {true, nil, nil}, {true, zBool, zBool}, {true, zInt, zInt}, {true, zInt8, zInt8}, {true, zInt16, zInt16}, {true, zInt32, zInt32}, {true, zInt64, zInt64}, {true, zUint, zUint}, {true, zUint8, zUint8}, {true, zUint16, zUint16}, {true, zUint32, zUint32}, {true, zUint64, zUint64}, {true, zUintptr, zUintptr}, {true, zFloat32, zFloat32}, {true, zFloat64, zFloat64}, {true, zArray0, zArray0}, {true, zArray1, zArray1}, {true, zChan, zChan}, {true, zFunc, zFunc}, {true, zIface, zIface}, {true, zMap, zBool}, {false, zMap, zInt}, {true, zSlice, zSlice}, {true, zString, zString}, {true, zStruct, zStruct}, {true, zBoolPtr, zBoolPtr}, {true, zIntPtr, zIntPtr}, {true, zInt8Ptr, zInt8Ptr}, {true, zInt16Ptr, zInt16Ptr}, {true, zInt32Ptr, zInt32Ptr}, {true, zInt64Ptr, zInt64Ptr}, {true, zUintPtr, zUintPtr}, {true, zUint8Ptr, zUint8Ptr}, {true, zUint16Ptr, zUint16Ptr}, {true, zUint32Ptr, zUint32Ptr}, {true, zUint64Ptr, zUint64Ptr}, {true, zUintptrPtr, zUintptrPtr}, {true, zFloat32Ptr, zFloat32Ptr}, {true, zFloat64Ptr, zFloat64Ptr}, {true, zArray0Ptr, zArray0Ptr}, {true, zArray1Ptr, zArray1Ptr}, {true, zChanPtr, zChanPtr}, {true, zFuncPtr, zFuncPtr}, {true, zIfacePtr, zIfacePtr}, {true, zMapPtr, zMapPtr}, {true, zSlicePtr, zSlicePtr}, {true, zStringPtr, zStringPtr}, {true, zStructPtr, zStructPtr}, {true, zMyInt, zMyInt}, {true, zMyString, zMyString}, {true, zJSON, zJSON}, {true, zJSONPtr, zJSONPtr}, {true, zTime, zTime}, } for i, v := range failures { msg := fmt.Sprintf("case %d: %#v, %#v", i, v.actual, v.expected) if v.panic { t.Panic(func() { t.HasKey(v.actual, v.expected) }, msg) } else { t.NotHasKey(v.actual, v.expected, msg) } } t.HasKey(map[int]string{2: "two", 5: "five", 10: "ten"}, 5) t.HasKey(map[string]int{"two": 2, "five": 5, "ten": 10}, "five") t.NotHasKey(map[string]int{"two": 2, "five": 5, "ten": 10}, "") } func TestCheckerZero(tt *testing.T) { t := check.T(tt) todo := t.TODO() cases := []struct { zero interface{} notzero interface{} }{ {zBool, xBool}, {zInt, xInt}, {zInt8, xInt8}, {zInt16, xInt16}, {zInt32, xInt32}, {zInt64, xInt64}, {zUint, xUint}, {zUint8, xUint8}, {zUint16, xUint16}, {zUint32, xUint32}, {zUint64, xUint64}, {zUintptr, xUintptr}, {zFloat32, xFloat32}, {zFloat64, xFloat64}, {zArray0, xArray1}, {zArray1, xArray1}, {zChan, xChan}, {zFunc, xFunc}, {zIface, xIface}, {zMap, xMap}, {zSlice, xSlice}, {zString, xString}, {zStruct, xStruct}, {zBoolPtr, xBoolPtr}, {zIntPtr, xIntPtr}, {zInt8Ptr, xInt8Ptr}, {zInt16Ptr, xInt16Ptr}, {zInt32Ptr, xInt32Ptr}, {zInt64Ptr, xInt64Ptr}, {zUintPtr, xUintPtr}, {zUint8Ptr, xUint8Ptr}, {zUint16Ptr, xUint16Ptr}, {zUint32Ptr, xUint32Ptr}, {zUint64Ptr, xUint64Ptr}, {zUintptrPtr, xUintptrPtr}, {zFloat32Ptr, xFloat32Ptr}, {zFloat64Ptr, xFloat64Ptr}, {zArray0Ptr, xArray1Ptr}, {zArray1Ptr, xArray1Ptr}, {zChanPtr, xChanPtr}, {zFuncPtr, xFuncPtr}, {zIfacePtr, xIfacePtr}, {zMapPtr, xMapPtr}, {zSlicePtr, xSlicePtr}, {zStringPtr, xStringPtr}, {zStructPtr, xStructPtr}, {zMyInt, xMyInt}, {zMyString, xMyString}, {zJSON, xJSON}, {zJSONPtr, xJSONPtr}, {zTime, xTime}, {nil, vChan}, {nil, vFunc}, {vIface, xIface}, {nil, vMap}, {nil, vSlice}, {[0][]int{}, [1][]int{{1}}}, {[2][]int{nil, nil}, [2][]int{nil, {}}}, {[2][2][2]int{1: {1: {1: 0}}}, [2][2][2]int{1: {1: {1: 1}}}}, } for i, v := range cases { msg := fmt.Sprintf("case %d: %#v, %#v", i, v.zero, v.notzero) t.Zero(v.zero, msg) todo.Zero(v.notzero, msg) t.NotZero(v.notzero, msg) todo.NotZero(v.zero, msg) } t.Zero(nil) todo.NotZero(nil) } func TestCheckerLen(tt *testing.T) { t := check.T(tt) todo := t.TODO() cases := []struct { panic bool actual interface{} len int }{ {true, nil, 0}, {true, zBool, 0}, {true, zInt, 0}, {true, zInt8, 0}, {true, zInt16, 0}, {true, zInt32, 0}, {true, zInt64, 0}, {true, zUint, 0}, {true, zUint8, 0}, {true, zUint16, 0}, {true, zUint32, 0}, {true, zUint64, 0}, {true, zUintptr, 0}, {true, zFloat32, 0}, {true, zFloat64, 0}, {false, zArray0, 1}, {false, zArray1, 0}, {false, zChan, 1}, {true, zFunc, 0}, {true, zIface, 0}, {false, zMap, 1}, {false, zSlice, 1}, {false, zString, 1}, {true, zStruct, 0}, {true, zBoolPtr, 0}, {true, zIntPtr, 0}, {true, zInt8Ptr, 0}, {true, zInt16Ptr, 0}, {true, zInt32Ptr, 0}, {true, zInt64Ptr, 0}, {true, zUintPtr, 0}, {true, zUint8Ptr, 0}, {true, zUint16Ptr, 0}, {true, zUint32Ptr, 0}, {true, zUint64Ptr, 0}, {true, zUintptrPtr, 0}, {true, zFloat32Ptr, 0}, {true, zFloat64Ptr, 0}, {true, zArray0Ptr, 0}, {true, zArray1Ptr, 0}, {true, zChanPtr, 0}, {true, zFuncPtr, 0}, {true, zIfacePtr, 0}, {true, zMapPtr, 0}, {true, zSlicePtr, 0}, {true, zStringPtr, 0}, {true, zStructPtr, 0}, {true, zMyInt, 0}, {false, zMyString, 1}, {false, zJSON, 1}, {true, zJSONPtr, 0}, {true, zTime, 0}, } for _, v := range cases { if v.panic { t.Panic(func() { t.Len(v.actual, v.len) }) } else { todo.Len(v.actual, v.len) t.NotLen(v.actual, v.len) } } t.Len(zArray0, 0) t.Len(zArray1, 1) c := make(chan int, 5) t.Len(c, 0) todo.NotLen(c, 0) c <- 42 t.Len(c, 1) todo.NotLen(c, 1) m := make(map[string]int, 10) t.Len(m, 0) m["one"] = 1 m["ten"] = 10 t.Len(m, 2) t.Len(json.RawMessage("тест"), 8) t.Len([]rune("тест"), 4) t.Len(myString("test"), 4) t.Len("тест", 8) } func TestCheckerOrdered(t *testing.T) { cases := []struct { panic bool min interface{} mid interface{} max interface{} }{ {true, nil, nil, nil}, {true, zBool, xBool, xBool}, {false, xInt, xInt + 1, xInt + 2}, {false, xInt8, xInt8 + 1, xInt8 + 2}, {false, xInt16, xInt16 + 1, xInt16 + 2}, {false, xInt32, xInt32 + 1, xInt32 + 2}, {false, xInt64, xInt64 + 1, xInt64 + 2}, {false, xUint, xUint + 1, xUint + 2}, {false, xUint8, xUint8 + 1, xUint8 + 2}, {false, xUint16, xUint16 + 1, xUint16 + 2}, {false, xUint32, xUint32 + 1, xUint32 + 2}, {false, xUint64, xUint64 + 1, xUint64 + 2}, {false, xUintptr, xUintptr + 1, xUintptr + 2}, {false, xFloat32, xFloat32 + 1, xFloat32 + 2}, {false, xFloat64, xFloat64 + 1, xFloat64 + 2}, {true, zArray0, zArray0, zArray0}, {true, zArray1, xArray1, xArray1}, {true, zChan, xChan, xChan}, {true, zFunc, xFunc, xFunc}, {true, zIface, xIface, xIface}, {true, zMap, xMap, xMap}, {true, zSlice, xSlice, xSlice}, {false, xString, xString + "1", xString + "2"}, {true, zStruct, xStruct, xStruct}, {true, zBoolPtr, xBoolPtr, xBoolPtr}, {true, zIntPtr, xIntPtr, xIntPtr}, {true, zInt8Ptr, xInt8Ptr, xInt8Ptr}, {true, zInt16Ptr, xInt16Ptr, xInt16Ptr}, {true, zInt32Ptr, xInt32Ptr, xInt32Ptr}, {true, zInt64Ptr, xInt64Ptr, xInt64Ptr}, {true, zUintPtr, xUintPtr, xUintPtr}, {true, zUint8Ptr, xUint8Ptr, xUint8Ptr}, {true, zUint16Ptr, xUint16Ptr, xUint16Ptr}, {true, zUint32Ptr, xUint32Ptr, xUint32Ptr}, {true, zUint64Ptr, xUint64Ptr, xUint64Ptr}, {true, zUintptrPtr, xUintptrPtr, xUintptrPtr}, {true, zFloat32Ptr, xFloat32Ptr, xFloat32Ptr}, {true, zFloat64Ptr, xFloat64Ptr, xFloat64Ptr}, {true, zArray0Ptr, zArray0Ptr, zArray0Ptr}, {true, zArray1Ptr, xArray1Ptr, xArray1Ptr}, {true, zChanPtr, xChanPtr, xChanPtr}, {true, zFuncPtr, xFuncPtr, xFuncPtr}, {true, zIfacePtr, xIfacePtr, xIfacePtr}, {true, zMapPtr, xMapPtr, xMapPtr}, {true, zSlicePtr, xSlicePtr, xSlicePtr}, {true, zStringPtr, xStringPtr, xStringPtr}, {true, zStructPtr, xStructPtr, xStructPtr}, {false, xMyInt, xMyInt + 1, xMyInt + 2}, {false, xMyString, xMyString + "1", xMyString + "2"}, {true, xJSON, xJSON, xJSON}, {true, xJSONPtr, xJSONPtr, xJSONPtr}, {false, xTime, xTime.Add(time.Millisecond), xTime.Add(time.Second)}, } t.Run("Less", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for _, v := range cases { actual, expected := v.min, v.max if v.panic { t.Panic(func() { t.Less(actual, expected) }) t.Panic(func() { t.LT(actual, expected) }) t.Panic(func() { t.LessOrEqual(actual, expected) }) t.Panic(func() { t.LE(actual, expected) }) } else { t.Less(actual, expected) t.LT(actual, expected) t.LessOrEqual(actual, expected) t.LessOrEqual(actual, actual) t.LE(actual, expected) t.LE(actual, actual) actual, expected = expected, actual todo.Less(actual, expected) todo.LT(actual, expected) todo.LessOrEqual(actual, expected) todo.LE(actual, expected) } } }) t.Run("Greater", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for _, v := range cases { actual, expected := v.min, v.max if v.panic { t.Panic(func() { t.Greater(actual, expected) }) t.Panic(func() { t.GT(actual, expected) }) t.Panic(func() { t.GreaterOrEqual(actual, expected) }) t.Panic(func() { t.GE(actual, expected) }) } else { todo.Greater(actual, expected) todo.GT(actual, expected) todo.GreaterOrEqual(actual, expected) todo.GE(actual, expected) actual, expected = expected, actual t.Greater(actual, expected) t.GT(actual, expected) t.GreaterOrEqual(actual, expected) t.GreaterOrEqual(actual, actual) t.GE(actual, expected) t.GE(actual, actual) } } }) t.Run("Between", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for _, v := range cases { min, mid, max := v.min, v.mid, v.max if v.panic { t.Panic(func() { t.Between(mid, min, max) }) t.Panic(func() { t.BetweenOrEqual(mid, min, max) }) t.Panic(func() { t.NotBetween(min, mid, max) }) t.Panic(func() { t.NotBetweenOrEqual(min, mid, max) }) } else { t.Between(mid, min, max) t.BetweenOrEqual(mid, min, max) t.BetweenOrEqual(mid, mid, max) t.BetweenOrEqual(mid, min, mid) todo.NotBetween(mid, min, max) todo.NotBetweenOrEqual(mid, min, max) todo.NotBetweenOrEqual(mid, mid, max) todo.NotBetweenOrEqual(mid, min, mid) t.NotBetween(min, mid, max) t.NotBetween(max, min, mid) t.NotBetweenOrEqual(min, mid, max) t.NotBetweenOrEqual(max, min, mid) todo.Between(min, mid, max) todo.Between(max, min, mid) todo.BetweenOrEqual(min, mid, max) todo.BetweenOrEqual(max, min, mid) } } }) } func TestCheckerApprox(t *testing.T) { cases := []struct { panic bool actual interface{} expected interface{} delta interface{} smape float64 }{ {true, nil, nil, nil, 0}, {true, zBool, xBool, xBool, 0}, {false, xInt, xInt + 5, 7, 10.0}, {false, xInt8, xInt8 + 5, 7, 50.0}, {false, xInt16, xInt16 + 5, 7, 20.0}, {false, xInt32, xInt32 + 5, 7, 10.0}, {false, xInt64, xInt64 + 5, 7, 5.0}, {false, xUint, xUint + 5, uint(7), 6.0}, {false, xUint8, xUint8 + 5, uint(7), 30.0}, {false, xUint16, xUint16 + 5, uint(7), 20.0}, {false, xUint32, xUint32 + 5, uint(7), 10.0}, {false, xUint64, xUint64 + 5, uint(7), 5.0}, {false, xUintptr, xUintptr + 5, uint(7), 0.0000001}, {false, xFloat32, xFloat32 - 5, 7.0, 50.0}, {false, xFloat64, xFloat64 + 5, 7.0, 33.0}, {true, zArray0, zArray0, zArray0, 0}, {true, zArray1, xArray1, xArray1, 0}, {true, zChan, xChan, xChan, 0}, {true, zFunc, xFunc, xFunc, 0}, {true, zIface, xIface, xIface, 0}, {true, zMap, xMap, xMap, 0}, {true, zSlice, xSlice, xSlice, 0}, {true, xString, xString, xString, 0}, {true, zStruct, xStruct, xStruct, 0}, {true, zBoolPtr, xBoolPtr, xBoolPtr, 0}, {true, zIntPtr, xIntPtr, xIntPtr, 0}, {true, zInt8Ptr, xInt8Ptr, xInt8Ptr, 0}, {true, zInt16Ptr, xInt16Ptr, xInt16Ptr, 0}, {true, zInt32Ptr, xInt32Ptr, xInt32Ptr, 0}, {true, zInt64Ptr, xInt64Ptr, xInt64Ptr, 0}, {true, zUintPtr, xUintPtr, xUintPtr, 0}, {true, zUint8Ptr, xUint8Ptr, xUint8Ptr, 0}, {true, zUint16Ptr, xUint16Ptr, xUint16Ptr, 0}, {true, zUint32Ptr, xUint32Ptr, xUint32Ptr, 0}, {true, zUint64Ptr, xUint64Ptr, xUint64Ptr, 0}, {true, zUintptrPtr, xUintptrPtr, xUintptrPtr, 0}, {true, zFloat32Ptr, xFloat32Ptr, xFloat32Ptr, 0}, {true, zFloat64Ptr, xFloat64Ptr, xFloat64Ptr, 0}, {true, zArray0Ptr, zArray0Ptr, zArray0Ptr, 0}, {true, zArray1Ptr, xArray1Ptr, xArray1Ptr, 0}, {true, zChanPtr, xChanPtr, xChanPtr, 0}, {true, zFuncPtr, xFuncPtr, xFuncPtr, 0}, {true, zIfacePtr, xIfacePtr, xIfacePtr, 0}, {true, zMapPtr, xMapPtr, xMapPtr, 0}, {true, zSlicePtr, xSlicePtr, xSlicePtr, 0}, {true, zStringPtr, xStringPtr, xStringPtr, 0}, {true, zStructPtr, xStructPtr, xStructPtr, 0}, {false, xMyInt, xMyInt + 5, 7, 0.01}, {true, xMyString, xMyString, xMyString, 0}, {true, xJSON, xJSON, xJSON, 0}, {true, xJSONPtr, xJSONPtr, xJSONPtr, 0}, {false, xTime, xTime.Add(5 * time.Second), 7 * time.Second, 0}, } t.Run("Delta", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for _, v := range cases { if v.panic { t.Panic(func() { t.InDelta(v.actual, v.expected, v.delta) }) t.Panic(func() { t.NotInDelta(v.actual, v.expected, v.delta) }) } else { t.InDelta(v.actual, v.expected, v.delta) t.InDelta(v.expected, v.actual, v.delta) todo.NotInDelta(v.actual, v.expected, v.delta) todo.NotInDelta(v.expected, v.actual, v.delta) t.NotInDelta(v.actual, v.expected, half(v.delta)) t.NotInDelta(v.expected, v.actual, half(v.delta)) todo.InDelta(v.actual, v.expected, half(v.delta)) todo.InDelta(v.expected, v.actual, half(v.delta)) } } }) t.Run("SMAPE", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for _, v := range cases { if v.panic || v.smape == 0 { t.Panic(func() { t.InSMAPE(v.actual, v.expected, v.smape) }) t.Panic(func() { t.NotInSMAPE(v.actual, v.expected, v.smape) }) } else { t.InSMAPE(v.actual, v.expected, v.smape) t.InSMAPE(v.expected, v.actual, v.smape) todo.NotInSMAPE(v.actual, v.expected, v.smape) todo.NotInSMAPE(v.expected, v.actual, v.smape) t.NotInSMAPE(v.actual, v.expected, half(v.smape).(float64)) t.NotInSMAPE(v.expected, v.actual, half(v.smape).(float64)) todo.InSMAPE(v.actual, v.expected, half(v.smape).(float64)) todo.InSMAPE(v.expected, v.actual, half(v.smape).(float64)) } } t.InSMAPE(0, 0, 0.5) t.InSMAPE(0.0, 0.0, 0.5) }) } func half(v interface{}) interface{} { if v, ok := v.(time.Duration); ok { return v / 2 } switch val := reflect.ValueOf(v); val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return val.Int() / 2 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return val.Uint() / 2 case reflect.Float32, reflect.Float64: return val.Float() / 2 } panic(fmt.Sprintf("can't get half from %#v", v)) } func TestCheckerSubstring(t *testing.T) { cases := []struct { panic bool actual interface{} prefix string suffix string }{ {true, xBool, "", ""}, {true, xInt, "", ""}, {true, xInt8, "", ""}, {true, xInt16, "", ""}, {true, xInt32, "", ""}, {true, xInt64, "", ""}, {true, xUint, "", ""}, {true, xUint8, "", ""}, {true, xUint16, "", ""}, {true, xUint32, "", ""}, {true, xUint64, "", ""}, {true, xUintptr, "", ""}, {true, xFloat32, "", ""}, {true, xFloat64, "", ""}, {true, zArray0, "", ""}, {true, xArray1, "", ""}, {true, xChan, "", ""}, {true, xFunc, "", ""}, {true, xIface, "", ""}, {true, xMap, "", ""}, {true, xSlice, "", ""}, {false, xString, ""}, {true, xStruct, "", ""}, {true, xBoolPtr, "", ""}, {true, xIntPtr, "", ""}, {true, xInt8Ptr, "", ""}, {true, xInt16Ptr, "", ""}, {true, xInt32Ptr, "", ""}, {true, xInt64Ptr, "", ""}, {true, xUintPtr, "", ""}, {true, xUint8Ptr, "", ""}, {true, xUint16Ptr, "", ""}, {true, xUint32Ptr, "", ""}, {true, xUint64Ptr, "", ""}, {true, xUintptrPtr, "", ""}, {true, xFloat32Ptr, "", ""}, {true, xFloat64Ptr, "", ""}, {true, zArray0Ptr, "", ""}, {true, xArray1Ptr, "", ""}, {true, xChanPtr, "", ""}, {true, xFuncPtr, "", ""}, {true, xIfacePtr, "", ""}, {true, xMapPtr, "", ""}, {true, xSlicePtr, "", ""}, {true, xStringPtr, "", ""}, {true, xStructPtr, "", ""}, {true, xMyInt, "", ""}, {false, xMyString, "xy", "yz"}, {false, xJSON, "{", "}"}, {true, xJSONPtr, "", ""}, {false, zTime, "0001-01-01", "UTC"}, {false, []byte("String"), "Str", "ing"}, {false, []rune("Symbol"), "Sym", "bol"}, {false, time.Sunday, "Sun", "day"}, {false, io.EOF, "EO", "OF"}, } substrings := []struct { prefix interface{} suffix interface{} }{ {"Sunday", "Monday"}, {[]byte("Sunday"), []byte("Monday")}, {[]rune("Sunday"), []rune("Monday")}, {time.Sunday, time.Monday}, {errors.New("Sunday"), errors.New("Monday")}, // nolint:golint } t.Run("HasPrefix", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for i, v := range cases { msg := fmt.Sprintf("case %d: %#v, %#v, %#v", i, v.actual, v.prefix, v.suffix) if v.panic { t.Panic(func() { t.HasPrefix(v.actual, v.prefix) }, msg) t.Panic(func() { t.NotHasPrefix(v.actual, v.prefix) }, msg) t.Panic(func() { t.HasPrefix("", v.actual) }, msg) t.Panic(func() { t.NotHasPrefix("", v.actual) }, msg) } else { t.HasPrefix(v.actual, v.prefix, msg) todo.HasPrefix(v.actual, v.suffix, msg) t.NotHasPrefix(v.actual, v.suffix, msg) todo.NotHasPrefix(v.actual, v.prefix, msg) } } for _, v := range substrings { t.HasPrefix("Sunday Monday", v.prefix) todo.NotHasPrefix("Sunday Monday", v.prefix) } todo.HasPrefix(nil, "") t.NotHasPrefix(nil, "") todo.HasPrefix("", nil) t.NotHasPrefix("", nil) t.HasPrefix("", "") todo.NotHasPrefix("", "") t.HasPrefix("x", "") todo.NotHasPrefix("x", "") }) t.Run("HasSuffix", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() for i, v := range cases { msg := fmt.Sprintf("case %d: %#v, %#v, %#v", i, v.actual, v.suffix, v.suffix) if v.panic { t.Panic(func() { t.HasSuffix(v.actual, v.suffix) }, msg) t.Panic(func() { t.NotHasSuffix(v.actual, v.suffix) }, msg) t.Panic(func() { t.HasSuffix("", v.actual) }, msg) t.Panic(func() { t.NotHasSuffix("", v.actual) }, msg) } else { t.HasSuffix(v.actual, v.suffix, msg) todo.HasSuffix(v.actual, v.prefix, msg) t.NotHasSuffix(v.actual, v.prefix, msg) todo.NotHasSuffix(v.actual, v.suffix, msg) } } for _, v := range substrings { t.HasSuffix("Sunday Monday", v.suffix) todo.NotHasSuffix("Sunday Monday", v.suffix) } todo.HasSuffix(nil, "") t.NotHasSuffix(nil, "") todo.HasSuffix("", nil) t.NotHasSuffix("", nil) t.HasSuffix("", "") todo.NotHasSuffix("", "") t.HasSuffix("x", "") todo.NotHasSuffix("x", "") }) } func TestJSONEqual(tt *testing.T) { t := check.T(tt) todo := t.TODO() cases := []struct { panic bool json interface{} }{ {false, nil}, {true, zBool}, {true, zInt}, {true, zInt8}, {true, zInt16}, {true, zInt32}, {true, zInt64}, {true, zUint}, {true, zUint8}, {true, zUint16}, {true, zUint32}, {true, zUint64}, {true, zUintptr}, {true, zFloat32}, {true, zFloat64}, {true, zArray0}, {true, zArray1}, {true, zChan}, {true, zFunc}, {false, zIface}, // nil {true, zMap}, {true, zSlice}, {false, zString}, {true, zStruct}, {true, zBoolPtr}, {true, zIntPtr}, {true, zInt8Ptr}, {true, zInt16Ptr}, {true, zInt32Ptr}, {true, zInt64Ptr}, {true, zUintPtr}, {true, zUint8Ptr}, {true, zUint16Ptr}, {true, zUint32Ptr}, {true, zUint64Ptr}, {true, zUintptrPtr}, {true, zFloat32Ptr}, {true, zFloat64Ptr}, {true, zArray0Ptr}, {true, zArray1Ptr}, {true, zChanPtr}, {true, zFuncPtr}, {true, zIfacePtr}, {true, zMapPtr}, {true, zSlicePtr}, {true, zStringPtr}, {true, zStructPtr}, {true, zMyInt}, {false, zMyString}, {false, zJSON}, {false, zJSONPtr}, {true, zTime}, {false, []byte(nil)}, {false, []byte{}}, } for i, v := range cases { if v.panic { t.Panic(func() { t.JSONEqual(v.json, `{}`, i) }) t.Panic(func() { t.JSONEqual(`{}`, v.json) }) } else { todo.JSONEqual(v.json, v.json) } } invalid := `{"a":1,"b":[2]` invalidRaw := json.RawMessage(invalid) todo.JSONEqual(invalid, invalid) todo.JSONEqual([]byte(invalid), []byte(invalid)) todo.JSONEqual(&invalidRaw, invalid) todo.JSONEqual(&invalidRaw, invalid+"}") todo.JSONEqual(invalidRaw, []byte(invalid)) t.JSONEqual(invalidRaw, invalidRaw) t.JSONEqual(&invalidRaw, &invalidRaw) t.JSONEqual(&invalidRaw, invalidRaw) t.JSONEqual(invalidRaw, &invalidRaw) validRaw := json.RawMessage(invalid + "}") valid := []interface{}{ `{ "b" : [ 2],"a" :1} `, []byte(` { "b": [2 ],"a": 1}`), validRaw, &validRaw, } for _, actual := range valid { for _, expected := range valid { t.JSONEqual(actual, expected) } } } func TestHasType(tt *testing.T) { t := check.T(tt) todo := t.TODO() vs := []interface{}{ zBool, zInt, zInt8, zInt16, zInt32, zInt64, zUint, zUint8, zUint16, zUint32, zUint64, zUintptr, zFloat32, zFloat64, zArray0, zArray1, zChan, zFunc, zIface, // nil zMap, zSlice, zString, zStruct, zBoolPtr, zIntPtr, zInt8Ptr, zInt16Ptr, zInt32Ptr, zInt64Ptr, zUintPtr, zUint8Ptr, zUint16Ptr, zUint32Ptr, zUint64Ptr, zUintptrPtr, zFloat32Ptr, zFloat64Ptr, zArray0Ptr, zArray1Ptr, zChanPtr, zFuncPtr, zIfacePtr, zMapPtr, zSlicePtr, zStringPtr, zStructPtr, zMyInt, zMyString, zJSON, zJSONPtr, zTime, } for i, actual := range vs { for j, expected := range vs { if i == j { t.HasType(actual, expected) todo.NotHasType(actual, expected) } else { t.NotHasType(actual, expected) todo.HasType(actual, expected) } } } t.HasType(vChan, zChan) t.HasType(vFunc, zFunc) t.HasType(vIface, zIntPtr) t.HasType(vMap, zMap) t.HasType(vSlice, zSlice) var reader io.Reader t.HasType(reader, nil) t.HasType(&reader, (*io.Reader)(nil)) t.NotHasType(&reader, nil) t.HasType(os.Stdin, (*os.File)(nil)) t.NotHasType(os.Stdin, &reader) t.HasType(true, zBool) t.HasType(42, zInt) t.HasType("test", zString) t.HasType([]byte("test"), []byte(nil)) t.HasType([]byte("test"), []byte{}) t.HasType(new(int), zIntPtr) t.NotHasType(json.RawMessage([]byte("test")), []byte("test")) } func TestCheckers(t *testing.T) { t.Run("Err", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() cases := []struct { err bool deepEqual bool equal bool actual error expected error }{ {true, true, true, nil, nil}, {false, false, false, (*net.OpError)(nil), &net.OpError{}}, {false, false, false, (*net.OpError)(nil), nil}, {false, false, false, nil, (*net.OpError)(nil)}, {true, true, true, (*net.OpError)(nil), (*net.OpError)(nil)}, {true, true, false, &net.OpError{}, &net.OpError{}}, {true, true, true, io.EOF, io.EOF}, {true, true, false, io.EOF, errors.New("EOF")}, {false, false, false, pkgerrors.New("EOF"), io.EOF}, {false, false, false, pkgerrors.New("EOF"), errors.New("EOF")}, {true, true, false, pkgerrors.New("EOF"), pkgerrors.New("EOF")}, {true, false, false, pkgerrors.WithStack(io.EOF), io.EOF}, {true, false, false, pkgerrors.Wrap(io.EOF, "wrapped"), io.EOF}, {true, false, false, pkgerrors.Wrap(io.EOF, "wrapped"), errors.New("EOF")}, {true, false, false, pkgerrors.Wrap(pkgerrors.Wrap(io.EOF, "wrapped"), "wrapped2"), io.EOF}, {true, false, false, fmt.Errorf("wrapped: %w", io.EOF), io.EOF}, {true, false, false, fmt.Errorf("wrapped: %w", io.EOF), errors.New("EOF")}, {true, false, false, fmt.Errorf("wrapped2: %w", fmt.Errorf("wrapped: %w", io.EOF)), io.EOF}, {true, false, false, fmt.Errorf("wrapped2: %w", pkgerrors.Wrap(io.EOF, "wrapped")), io.EOF}, {true, false, false, pkgerrors.Wrap(fmt.Errorf("wrapped: %w", io.EOF), "wrapped2"), io.EOF}, {true, false, false, pkgerrors.Wrap(pkgerrors.Wrap(fmt.Errorf("wrapped4: %w", fmt.Errorf("wrapped3: %w", pkgerrors.Wrap(fmt.Errorf("wrapped: %w", io.EOF), "wrapped2"))), "wrapped5"), "wrapped6"), io.EOF}, {false, false, false, io.EOF, &myError{"EOF"}}, } for _, v := range cases { t.Run("", func(tt *testing.T) { t := check.T(tt) if v.err { t.Err(v.actual, v.expected) todo.NotErr(v.actual, v.expected) } else { todo.Err(v.actual, v.expected) t.NotErr(v.actual, v.expected) } if v.equal { t.Equal(v.actual, v.expected) } else { t.NotEqual(v.actual, v.expected) } if v.deepEqual { t.DeepEqual(v.actual, v.expected) } else { t.NotDeepEqual(v.actual, v.expected) } }) } }) t.Run("Panic", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() todo.Panic(func() {}) t.NotPanic(func() {}) t.Panic(func() { panic(nil) }) todo.NotPanic(func() { panic(nil) }) t.Panic(func() { panic("") }) t.Panic(func() { panic("oops") }) t.Panic(func() { panic(t) }) todo.NotPanic(func() { panic("") }) todo.NotPanic(func() { panic("oops") }) todo.NotPanic(func() { panic(t) }) }) t.Run("PanicMatch", func(tt *testing.T) { t := check.T(tt) todo := t.TODO() t.Parallel() t.Panic(func() { t.PanicMatch(func() { panic(0) }, nil) }) t.Panic(func() { t.PanicMatch(func() { panic(0) }, t) }) t.NotPanic(func() { t.PanicMatch(func() { panic(0) }, `0`) }) todo.PanicMatch(func() {}, ``) todo.PanicNotMatch(func() {}, ``) todo.PanicMatch(func() {}, `test`) todo.PanicNotMatch(func() {}, `test`) t.PanicMatch(func() { panic(nil) }, ``) todo.PanicNotMatch(func() { panic(nil) }, ``) t.PanicMatch(func() { panic(nil) }, `^$`) todo.PanicNotMatch(func() { panic(nil) }, `^$`) t.PanicNotMatch(func() { panic(nil) }, `test`) todo.PanicMatch(func() { panic(nil) }, `test`) t.PanicMatch(func() { panic("") }, regexp.MustCompile(`^$`)) t.PanicMatch(func() { panic("oops") }, `(?i)Oops`) t.PanicMatch(func() { panic(t) }, `^&check.C{`) t.PanicNotMatch(func() { panic("") }, regexp.MustCompile(`.`)) t.PanicNotMatch(func() { panic("oops") }, `(?-i)Oops`) todo.PanicNotMatch(func() { panic(t) }, `^&check.C{`) }) t.Run("Implements", func(tt *testing.T) { t := check.T(tt) t.Parallel() t.Implements(t, (*testing.TB)(nil)) t.Implements(os.Stdin, (*io.Reader)(nil)) t.Implements(os.Stdin, &xIface) t.Implements(*os.Stdin, (*io.Reader)(nil)) t.Implements(time.Time{}, (*fmt.Stringer)(nil)) t.Implements(&time.Time{}, (*fmt.Stringer)(nil)) t.NotImplements(os.Stdin, (*fmt.Stringer)(nil)) t.NotImplements(&os.Stdin, (*io.Reader)(nil)) t.NotImplements(new(int), (*io.Reader)(nil)) }) } check-1.2.1/color.go000066400000000000000000000014471356066224300142520ustar00rootroot00000000000000package check import ( "os" "strings" ) var ( ansiGreen = "\033[32m" ansiYellow = "\033[33m" ansiRed = "\033[31m" ansiReset = "\033[0m" ) func init() { if !wantColor() { ansiGreen, ansiYellow, ansiRed, ansiReset = "", "", "", "" } } func wantColor() bool { return strings.Contains(os.Getenv("TERM"), "color") && (isTerminal() || os.Getenv("GO_TEST_COLOR") != "") } func colouredDiff(diff string) string { lines := strings.SplitAfter(diff, "\n") for i := range lines { switch { case strings.HasPrefix(lines[i], "--- "): case strings.HasPrefix(lines[i], "+++ "): case strings.HasPrefix(lines[i], "-"): lines[i] = ansiGreen + lines[i] + ansiReset case strings.HasPrefix(lines[i], "+"): lines[i] = ansiRed + lines[i] + ansiReset } } return strings.Join(lines, "") } check-1.2.1/color_bsd.go000066400000000000000000000003401356066224300150710ustar00rootroot00000000000000// +build darwin dragonfly freebsd netbsd openbsd package check import ( "os" "golang.org/x/sys/unix" ) func isTerminal() bool { _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TIOCGETA) return err == nil } check-1.2.1/color_linux.go000066400000000000000000000002741356066224300154660ustar00rootroot00000000000000// +build linux package check import ( "os" "golang.org/x/sys/unix" ) func isTerminal() bool { _, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETS) return err == nil } check-1.2.1/color_other.go000066400000000000000000000002001356066224300154350ustar00rootroot00000000000000// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!windows package check func isTerminal() bool { return false } check-1.2.1/color_windows.go000066400000000000000000000003041356066224300160130ustar00rootroot00000000000000// +build windows package check import ( "os" "syscall" ) func isTerminal() bool { var mode uint32 err := syscall.GetConsoleMode(syscall.Handle(os.Stdout.Fd()), &mode) return err == nil } check-1.2.1/doc.go000066400000000000000000000112211356066224300136700ustar00rootroot00000000000000// Package check provide helpers to complement Go testing package. // // Features // // This package is like testify/assert on steroids. :) // // - Compelling output from failed tests: // - Very easy-to-read dumps for expected and actual values. // - Same text diff you loved in testify/assert. // - Also visual diff in GoConvey web UI, if you use it (recommended). // - Statistics with amount of passed/failed checks. // - Colored output in terminal. // - 100% compatible with testing package - check package just provide // convenient wrappers for *testing.T methods and doesn't introduce // new concepts like BDD, custom test suite or unusual execution flow. // - All checks you may ever need! :) // - Very easy to add your own check functions. // - Concise, handy and consistent API, without dot-import! // // Quickstart // // Just wrap each (including subtests) *testing.T using check.T() and write // tests as usually with testing package. Call new methods provided by // this package to have more clean/concise test code and cool dump/diff. // // import "github.com/powerman/check" // // func TestSomething(tt *testing.T) { // t := check.T(tt) // t.Equal(2, 2) // t.Log("You can use new t just like usual *testing.T") // t.Run("Subtests/Parallel example", func(tt *testing.T) { // t := check.T(tt) // t.Parallel() // t.NotEqual(2, 3, "should not be 3!") // obj, err := NewObj() // if t.Nil(err) { // t.Match(obj.field, `^\d+$`) // } // }) // } // // To get optional statistics about executed checkers add: // // func TestMain(m *testing.M) { check.TestMain(m) } // // When use goconvey tool, to get nice diff in web UI add: // // import _ "github.com/smartystreets/goconvey/convey" // // Hints // // ★ How to check for errors: // // // If you just want nil: // t.Nil(err) // t.Err(err, nil) // // // Check for (absence of) concrete (possibly wrapped) error: // t.Err(err, io.EOF) // t.NotErr(err, io.EOF) // nil is not io.EOF, so it's ok too // // // When need to match by error's text: // t.Match(err, "file.*permission") // // // Use Equal ONLY when checking for same instance: // t.Equal(io.EOF, io.EOF) // this works // t.Equal(io.EOF, errors.New("EOF")) // this doesn't work! // t.Err(io.EOF, errors.New("EOF")) // this works // t.DeepEqual(io.EOF, errors.New("EOF")) // this works too // // ★ Each check returns bool, so you can easily skip problematic code: // // if t.Nil(err) { // t.Match(obj.field, `^\d+$`) // } // // ★ You can turn any check into assertion to stop test immediately: // // t.Must(t.Nil(err)) // // ★ You can provide extra description to each check: // // t.Equal(got, want, "Just msg: will Print(), % isn't special") // t.Equal(got, want, "Msg with args: will Printf(): %v", extra) // // ★ There are short synonyms for checks implementing usual ==, !=, etc.: // // t.EQ(got, want) // same as t.Equal // t.NE(got, want) // same as t.NotEqual // t.LT(got, want) // same as t.Less // t.LE(got, want) // same as t.LessOrEqual // t.GT(got, want) // same as t.Greater // t.GE(got, want) // same as t.GreaterOrEqual // // ★ If you need custom check, which isn't available out-of-box - see // Should checker, it'll let you plug in your own checker with ease. // // ★ It will panic when called with arg of wrong type - because this // means bug in your test. // // ★ If you don't see colors in `go test` output it may happens because of // two reasons: either your $TERM doesn't contain substring "color" or // you're running `go test path/to/your/package`. To force colored output // in last case just set this environment variable: // // export GO_TEST_COLOR=1 // // Contents // // There are few special functions (assertion, custom checkers, etc.). // // Must // Should // TODO // // Everything else are just trivial (mostly) checkers which works in // obvious way and accept values of any types which makes sense (and // panics on everything else). // // Nil NotNil // Zero NotZero // True False // // Equal NotEqual EQ NE // DeepEqual NotDeepEqual // Err NotErr // BytesEqual NotBytesEqual // JSONEqual // // Greater LessOrEqual GT LE // Less GreaterOrEqual LT GE // Between NotBetween // BetweenOrEqual NotBetweenOrEqual // InDelta NotInDelta // InSMAPE NotInSMAPE // // Len NotLen // Match NotMatch // HasPrefix NotHasPrefix // HasSuffix NotHasSuffix // HasKey NotHasKey // Contains NotContains // // HasType NotHasType // Implements NotImplements // // Panic NotPanic // PanicMatch PanicNotMatch package check check-1.2.1/dump.go000066400000000000000000000073431356066224300141020ustar00rootroot00000000000000package check import ( "bytes" "encoding/json" "fmt" "reflect" "strconv" "strings" "unicode/utf8" "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" ) var spewCfg = spew.ConfigState{ Indent: " ", DisablePointerAddresses: true, DisableCapacities: true, SortKeys: true, SpewKeys: true, } type dump struct { dump string indirectType reflect.Type } // String returns dump of value given to newDump. func (v dump) String() string { return v.dump } func (v dump) diff(expected dump) string { if v.indirectType != expected.indirectType { return "" } if !strings.ContainsRune(v.dump[:len(v.dump)-1], '\n') && !strings.ContainsRune(expected.dump[:len(expected.dump)-1], '\n') { return "" } diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ A: difflib.SplitLines(expected.dump), B: difflib.SplitLines(v.dump), FromFile: "Expected", FromDate: "", ToFile: "Actual", ToDate: "", Context: 1, }) if err != nil { return "" } return "Diff:\n" + diff } // newDump prepare i dump using spew.Sdump in most cases and custom // improved dump for these cases: // - nil: remove "(interface{})" prefix // - byte: use 0xFF instead of decimal // - rune: use quoted char instead of number for valid runes // - string: use this instead of quoted single-line: // - valid utf8: don't quote ", show multiline strings on separate lines // - invalid utf8: use hexdump like for []byte // - []byte: same as string instead of hexdump for valid utf8 // - []rune: use quoted char instead of number for valid runes in list // - json.RawMessage: indent, then same as string func newDump(i interface{}) (d dump) { // nolint:gocyclo,gocognit,funlen d.dump = spewCfg.Sdump(i) if i == nil { d.dump = "\n" return d } val := reflect.ValueOf(i) typ := reflect.TypeOf(i) kind := typ.Kind() if kind == reflect.Ptr { if val.IsNil() { return d } val = val.Elem() typ = typ.Elem() kind = typ.Kind() } d.indirectType = typ switch { case typ == reflect.TypeOf(json.RawMessage(nil)): v := val.Bytes() var buf bytes.Buffer if json.Indent(&buf, v, "", " ") == nil { d.dump = fmt.Sprintf("(%T) (len=%d) '\n%s\n'\n", i, len(v), buf.String()) } case kind == reflect.Uint8: v := byte(val.Uint()) d.dump = fmt.Sprintf("(%T) 0x%02X\n", i, v) case kind == reflect.Int32: v := rune(val.Int()) if utf8.ValidRune(v) { d.dump = fmt.Sprintf("(%T) %q\n", i, v) } case kind == reflect.Slice && typ.Elem().Kind() == reflect.Int32: valid := true for k := 0; k < val.Len() && valid; k++ { valid = valid && utf8.ValidRune(rune(val.Index(k).Int())) } if valid { d.dump = fmt.Sprintf("(%T) %q\n", i, i) } case kind == reflect.String: v := val.String() if utf8.ValidString(v) { d.dump = fmt.Sprintf("(%T) (len=%d) %s\n", i, len(v), quote(v)) } else { d.dump = strings.Replace(spewCfg.Sdump([]byte(v)), "([]uint8)", fmt.Sprintf("(%T)", i), 1) } case kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8: v := val.Bytes() if len(v) > 0 && utf8.Valid(v) || len(v) == 0 && !val.IsNil() { d.dump = fmt.Sprintf("(%T) (len=%d) %s\n", i, len(v), quote(string(v))) } } return d } // quote like %#v, except keep \n and " unquoted for readability. func quote(s string) string { r := []rune(strconv.Quote(s)) q := r[:0] var multiline, esc bool for _, c := range r[1 : len(r)-1] { if esc { esc = false switch c { case 'n': c = '\n' multiline = true case '"': default: q = append(q, '\\') } } else if c == '\\' { esc = true continue } q = append(q, c) } if multiline { return fmt.Sprintf("'\n%s\n'", string(q)) } return fmt.Sprintf("'%s'", string(q)) } check-1.2.1/dump_test.go000066400000000000000000000054771356066224300151470ustar00rootroot00000000000000package check import ( "encoding/json" "io" "testing" "time" _ "github.com/smartystreets/goconvey/convey" ) func TestDump(tt *testing.T) { t := T(tt) type ( myBool bool myInt int myInt32 int32 myInt64 int64 myUint uint myUint64 uint64 myByte byte myRune rune myUintptr uintptr myFloat64 float64 myString string myRunes []rune myBytes []byte myStruct struct { i int s string } ) var ( j = json.RawMessage(`[{"key":"one","value":1},{"key":"two","value":2}]`) jnil *json.RawMessage ) cases := []struct { improved bool i interface{} }{ {true, nil}, {false, true}, {false, myBool(true)}, {false, -42}, {false, myInt(-42)}, {false, int32(-32)}, {false, myInt32(-32)}, {false, int64(-64)}, {false, myInt64(-64)}, {false, uint(42)}, {false, myUint(42)}, {false, uint64(64)}, {false, myUint64(64)}, {true, byte(10)}, {true, myByte(10)}, {true, byte(255)}, {true, myByte(255)}, {true, rune(0)}, {true, myRune(0)}, {true, ' '}, {true, myRune(' ')}, {true, ' '}, {true, myRune(' ')}, {true, '\n'}, {true, myRune('\n')}, {true, '€'}, {true, myRune('€')}, {false, uintptr(0)}, {false, myUintptr(0)}, {false, uintptr(42)}, {false, myUintptr(42)}, {false, 0.0}, {false, myFloat64(0.0)}, {false, time.Monday}, {false, [0]int{}}, {false, [2]int{}}, {false, []int(nil)}, {false, []int{}}, {false, []int{1: 0}}, {false, chan int(nil)}, {false, make(chan int)}, {false, (chan<- int)(make(chan int, 2))}, {false, (func())(nil)}, {false, func(i int) int { return 0 }}, {false, io.EOF}, {false, map[int]int(nil)}, {false, map[int]int{2: 0}}, {false, make(map[int]int, 2)}, {false, (*int)(nil)}, {true, ""}, {true, myString("")}, {true, " "}, {true, myString(" ")}, {true, "\\`'\""}, {true, myString("\\`'\"")}, {true, "€"}, {true, myString("€")}, {true, "\x01\x02\x03\n\xff\xff"}, {true, myString("\x01\x02\x03\n\xff\xff")}, {true, "line1\nline2"}, {true, myString("line1\nline2")}, {false, []byte(nil)}, {false, myBytes(nil)}, {true, []byte{}}, {true, myBytes{}}, {false, []byte("\x01\x02\x03\n\xff\xff")}, {false, myBytes("\x01\x02\x03\n\xff\xff")}, {true, []byte("line1\nvery long line2")}, {true, myBytes("line1\nvery long line2")}, {true, j}, {true, myBytes(j)}, {false, jnil}, {true, &j}, {true, []rune{}}, {true, myRunes{}}, {true, []rune{0, ' ', ' ', '\n', '€'}}, {true, myRunes{0, ' ', ' ', '\n', '€'}}, {false, time.Time{}}, {false, time.Now()}, {false, struct { i int s string }{0, ""}}, {false, myStruct{0, ""}}, } for _, v := range cases { dumpOld, dumpNew := spewCfg.Sdump(v.i), newDump(v.i).String() if v.improved { t.NotEqual(dumpNew, dumpOld) } else { t.Equal(dumpNew, dumpOld) } } } check-1.2.1/flags.go000066400000000000000000000004761356066224300142310ustar00rootroot00000000000000package check import ( "flag" "sync" ) type peekFlags struct { sync.Once conveyJSON bool } var flags peekFlags func (p *peekFlags) detect() *peekFlags { flags.Do(func() { flag.Visit(func(f *flag.Flag) { if f.Name == "convey-json" { p.conveyJSON = f.Value.String() == "true" } }) }) return p } check-1.2.1/go.mod000066400000000000000000000006361356066224300137120ustar00rootroot00000000000000module github.com/powerman/check go 1.13 require ( github.com/davecgh/go-spew v1.1.1 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect github.com/pkg/errors v0.8.1 github.com/pmezard/go-difflib v1.0.0 github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect github.com/smartystreets/goconvey v1.6.4 golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c ) check-1.2.1/go.sum000066400000000000000000000055131356066224300137360ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0= github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09 h1:IlD35wZE03o2qJy2o37WIskL33b7PT6cHdGnE8bieZs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE= golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= check-1.2.1/goconvey.go000066400000000000000000000015321356066224300147600ustar00rootroot00000000000000package check import ( "bytes" "encoding/json" "errors" "fmt" "os" "github.com/smartystreets/goconvey/convey/reporting" ) var errNoGoConvey = errors.New("goconvey not detected") func reportToGoConvey(actual, expected, failure string) error { if !flags.detect().conveyJSON { return errNoGoConvey } testFile, testLine, funcLine := callerTestFileLines() report := reporting.ScopeResult{ File: testFile, Line: funcLine, Assertions: []*reporting.AssertionResult{{ File: testFile, Line: testLine, Expected: expected, Actual: actual, Failure: failure, }}, } var buf bytes.Buffer fmt.Fprintln(&buf, reporting.OpenJson) if err := json.NewEncoder(&buf).Encode(report); err != nil { return err } fmt.Fprintln(&buf, ",") fmt.Fprintln(&buf, reporting.CloseJson) _, err := buf.WriteTo(os.Stdout) return err } check-1.2.1/stats.go000066400000000000000000000044571356066224300142760ustar00rootroot00000000000000package check import ( "fmt" "os" "sort" "strings" "sync" "testing" ) type counter struct { name string value int force bool color string size int } func (c counter) String() (s string) { if c.value != 0 || c.force { color := c.color if c.value == 0 { color = ansiReset } s = fmt.Sprintf("%s%*d %s%s", color, c.size, c.value, c.name, ansiReset) } else { s = strings.Repeat(" ", c.size+1+len(c.name)) } return s } type testStat struct { name string passed counter forged counter failed counter } func newTestStat(desc string, force bool) *testStat { return &testStat{ name: desc, passed: counter{force: force, name: "passed", color: ansiGreen}, forged: counter{force: force, name: "todo", color: ansiYellow}, failed: counter{force: force, name: "failed", color: ansiRed}, } } func (c testStat) String() string { return fmt.Sprintf("checks: %s %s %s\t%s", c.passed, c.forged, c.failed, c.name) } var statsMu sync.Mutex var stats = map[*testing.T]*testStat{} // Report output statistics about passed/failed checks. // It should be called from TestMain after m.Run(), for ex.: // // func TestMain(m *testing.M) { // code := m.Run() // check.Report() // os.Exit(code) // } // // If this is all you need - just use TestMain instead. func Report() { statsMu.Lock() defer statsMu.Unlock() total := newTestStat("(total)", true) ts := make([]*testing.T, 0, len(stats)) for t := range stats { ts = append(ts, t) total.passed.value += stats[t].passed.value total.forged.value += stats[t].forged.value total.failed.value += stats[t].failed.value } total.passed.size = digits(total.passed.value) total.forged.size = digits(total.forged.value) total.failed.size = digits(total.failed.value) if testing.Verbose() { sort.Slice(ts, func(a, b int) bool { return ts[a].Name() < ts[b].Name() }) for _, t := range ts { stats[t].passed.size = total.passed.size stats[t].forged.size = total.forged.size stats[t].failed.size = total.failed.size fmt.Printf(" %s\n", stats[t]) } } fmt.Printf(" %s\n", total) } // TestMain provides same default implementation as used by testing // package with extra Report call to output statistics. Usage: // // func TestMain(m *testing.M) { check.TestMain(m) } func TestMain(m *testing.M) { code := m.Run() Report() os.Exit(code) } check-1.2.1/stats_main_test.go000066400000000000000000000001731356066224300163300ustar00rootroot00000000000000package check_test import ( "testing" "github.com/powerman/check" ) func TestMain(m *testing.M) { check.TestMain(m) } check-1.2.1/util.go000066400000000000000000000026031356066224300141040ustar00rootroot00000000000000package check import ( "fmt" "math" "path/filepath" "reflect" "runtime" "strings" ) func callerTestFileLines() (file string, line int, funcLine int) { pc, file, line, ok := runtime.Caller(0) myfile := file for stack := 1; ok && samePackage(myfile, file); stack++ { pc, file, line, ok = runtime.Caller(stack) } if f := runtime.FuncForPC(pc); f != nil { _, funcLine = f.FileLine(f.Entry()) } return file, line, funcLine } func samePackage(basefile, file string) bool { return filepath.Dir(basefile) == filepath.Dir(file) && !strings.HasSuffix(file, "_test.go") } func callerFuncName(stack int) string { pc, _, _, _ := runtime.Caller(stack + 1) return strings.TrimPrefix(funcNameAt(pc), "(*C).") } func funcName(f interface{}) string { return funcNameAt(reflect.ValueOf(f).Pointer()) } func funcNameAt(pc uintptr) string { name := "" if f := runtime.FuncForPC(pc); f != nil { name = f.Name() if i := strings.LastIndex(name, "/"); i != -1 { name = name[i+1:] } if i := strings.Index(name, "."); i != -1 { name = name[i+1:] } } return name } func format(msg ...interface{}) string { if len(msg) > 1 { return fmt.Sprintf(msg[0].(string), msg[1:]...) } return fmt.Sprint(msg...) } // digits return amount of decimal digits in number. func digits(number int) int { if number == 0 { return 1 } return int(math.Floor(math.Log10(float64(number)) + 1)) } check-1.2.1/util_test.go000066400000000000000000000011271356066224300151430ustar00rootroot00000000000000package check import ( "regexp" "testing" ) func TestFormat(tt *testing.T) { t := T(tt) cases := []struct { args []interface{} want string }{ {[]interface{}{}, ""}, {[]interface{}{"msg"}, "msg"}, {[]interface{}{"%s", "msg"}, "msg"}, {[]interface{}{"one", "two"}, "one%!(EXTRA string=two)"}, {[]interface{}{42}, "42"}, {[]interface{}{regexp.MustCompile(".*")}, ".*"}, } for i, v := range cases { t.Equal(format(v.args...), v.want, i) } } func TestCaller(tt *testing.T) { t := T(tt) t.Equal(callerFuncName(0), "TestCaller") t.Equal(callerFuncName(1000), "") }