diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index e90f369..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,46 +0,0 @@ -version: 2 -jobs: - # Test validation for semantic-release - semantic-release: - docker: - - image: circleci/node:17 - steps: - - checkout - - run: sudo npm install -g semantic-release@17.0.4 @semantic-release/exec@5.0.0 - - run: semantic-release --ci --dry-run - lint: - docker: - - image: circleci/golang:1.15 - working_directory: /go/src/github.com/oxyno-zeta/gomock-extra-matcher - steps: - - checkout - - run: make code/lint - - save_cache: - key: go-mod-{{ checksum "go.mod" }} - paths: - - "/go/pkg/" - test: - docker: - - image: circleci/golang:1.15 - working_directory: /go/src/github.com/oxyno-zeta/gomock-extra-matcher - steps: - - checkout - - restore_cache: - keys: - - go-mod-{{ checksum "go.mod" }} - - run: make test/unit - - run: make test/coverage - - run: mv coverage.html /tmp/artifacts - - store_artifacts: - path: /tmp/artifacts - - run: go get -u github.com/mattn/goveralls - - run: |- - export PATH=$GOBIN:$PATH - goveralls -coverprofile=c.out -service=circle-ci -repotoken=$COVERALLS_TOKEN -workflows: - version: 2 - project: - jobs: - - semantic-release - - lint - - test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a908f09 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: ci +on: + push: + branches-ignore: + - "github-pages" + pull_request: {} +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: golangci/golangci-lint-action@v2 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.43.0 + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the action will use pre-installed Go. + # skip-go-installation: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true + test: + runs-on: ubuntu-latest + needs: + - lint + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: "^1.17.0" + - run: make test/unit + - run: make test/coverage + - run: go get github.com/mattn/goveralls + - env: + COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: goveralls -coverprofile=c.out -service=github diff --git a/.golangci.yaml b/.golangci.yaml index 0b7d592..97143c4 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -330,11 +330,9 @@ linters: - typecheck - bodyclose - noctx - - golint - rowserrcheck - stylecheck - gosec - - interfacer - unconvert - dupl - goconst @@ -342,14 +340,12 @@ linters: - gofmt - goimports - goheader - - maligned - depguard - misspell - lll - unparam - dogsled - nakedret - - scopelint - gocritic - gochecknoinits - godox @@ -364,6 +360,17 @@ linters: - sqlclosecheck - nlreturn - nolintlint + - forcetypeassert + - gomoddirectives + - importas + - nilerr + - promlinter + - revive + - wastedassign + - bidichk + - contextcheck + - nilnil + - tenv disable: - prealloc - gochecknoglobals @@ -375,6 +382,9 @@ linters: - gci - goerr113 # Forbid dynamic errors - nestif + - scopelint + - ireturn + - varnamelen disable-all: false presets: - bugs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..71b88fa --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,51 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/commitizen-tools/commitizen + rev: v2.20.0 + hooks: + - id: commitizen + stages: [commit-msg] + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: check-json + stages: [commit] + - id: check-merge-conflict + stages: [commit] + - id: trailing-whitespace + stages: [commit] + - id: end-of-file-fixer + stages: [commit] + - id: check-yaml + stages: [commit] + - id: check-added-large-files + stages: [commit] + - id: check-executables-have-shebangs + stages: [commit] + - id: detect-aws-credentials + stages: [commit] + - id: detect-private-key + stages: [commit] + - repo: https://github.com/pre-commit/mirrors-prettier + # Use the sha or branch you want to point at + rev: v2.4.1 + hooks: + - id: prettier + stages: [commit] + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.8.0.1 + hooks: + - id: shellcheck + stages: [commit] + - repo: local + hooks: + - id: backend-lint + stages: [commit] + files: \.go$ + name: Backend Lint + entry: make + args: + - code/lint + require_serial: true + language: system diff --git a/Makefile b/Makefile index cd824f9..284f87d 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ ifndef HAS_GOLANGCI_LINT ifndef HAS_CURL $(error You must install curl) endif - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.30.0 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.43.0 endif ifndef HAS_GIT $(error You must install Git) diff --git a/README.md b/README.md index e1f17b9..e505266 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,18 @@ type Fake struct { mock.EXPECT().DoSomething(extra.StructMatcher().Field("Name", "value1").Field("Data", gomock.Eq(map[string]string{"fake":"value"}))) ``` +### OrMatcher + +#### Explanation + +This matcher will allow to test multiple matchers with a logical "OR" between them. This will stop at first match. + +#### Example + +```go +mock.EXPECT().DoSomething(extra.OrMatcher(gomock.Eq(1), gomock.Eq(10), gomock.Eq(15))) +``` + ## Thanks - My wife BH to support me doing this diff --git a/go.sum b/go.sum index f5c63ec..d067127 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,9 @@ -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= @@ -21,7 +18,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/map-matcher.go b/map-matcher.go index 95cfa34..97853ee 100644 --- a/map-matcher.go +++ b/map-matcher.go @@ -78,11 +78,11 @@ func (m *mapMatcher) Matches(x interface{}) bool { // Store if matcher key can be found matchKeyFound := false // Loop over map keys - for _, kVal := range rval.MapKeys() { + for _, keyVal := range rval.MapKeys() { // Get key data - keyD := kVal.Interface() + keyD := keyVal.Interface() // Get reflect value from key - rv := rval.MapIndex(kVal) + rv := rval.MapIndex(keyVal) // Get data from key val := rv.Interface() // Check if matcher key is matching current key diff --git a/or-matcher.go b/or-matcher.go new file mode 100644 index 0000000..099b8e3 --- /dev/null +++ b/or-matcher.go @@ -0,0 +1,58 @@ +package extra + +import ( + "fmt" + + "github.com/golang/mock/gomock" +) + +type orMatcher struct { + matchers []gomock.Matcher +} + +func (om *orMatcher) String() string { + if len(om.matchers) == 0 { + return "the \"or\" matcher will return false because list is empty" + } + + // Initialize string + str := "" + + // Loop over matchers + for i, m := range om.matchers { + // Ignore the first item + if i > 0 { + str += " or " + } + + // Concat matcher string + str += fmt.Sprintf("(%s)", m.String()) + } + + return str +} + +func (om *orMatcher) Matches(x interface{}) bool { + // Check empty case + if len(om.matchers) == 0 { + return false + } + + // Loop over matchers + for _, m := range om.matchers { + // Check if matcher is ok + if m.Matches(x) { + // Matches so ... End + return true + } + } + + // No match until now + // Or is false + return false +} + +// OrMatcher will return a new Or matcher. +func OrMatcher(matchers ...gomock.Matcher) gomock.Matcher { + return &orMatcher{matchers: matchers} +} diff --git a/or-matcher_test.go b/or-matcher_test.go new file mode 100644 index 0000000..6077be6 --- /dev/null +++ b/or-matcher_test.go @@ -0,0 +1,176 @@ +package extra + +import ( + "reflect" + "testing" + + "github.com/golang/mock/gomock" +) + +func Test_orMatcher_String(t *testing.T) { + type fields struct { + matchers []gomock.Matcher + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "empty", + fields: fields{}, + want: "the \"or\" matcher will return false because list is empty", + }, + { + name: "1 element", // Even if it weird + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Any(), + }, + }, + want: "(is anything)", + }, + { + name: "multiple elements", + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Any(), + gomock.Eq(true), + }, + }, + want: "(is anything) or (is equal to true (bool))", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + om := &orMatcher{ + matchers: tt.fields.matchers, + } + if got := om.String(); got != tt.want { + t.Errorf("orMatcher.String() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_orMatcher_Matches(t *testing.T) { + type fields struct { + matchers []gomock.Matcher + } + type args struct { + x interface{} + } + tests := []struct { + name string + fields fields + args args + want bool + }{ + { + name: "empty", + fields: fields{ + matchers: []gomock.Matcher{}, + }, + args: args{x: false}, + want: false, + }, + { + name: "1 element (ok)", // Even if it is weird + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Any(), + }, + }, + args: args{x: false}, + want: true, + }, + { + name: "1 element (ko)", // Even if it is weird + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Eq(true), + }, + }, + args: args{x: false}, + want: false, + }, + { + name: "multiple elements (all ok)", + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Any(), + gomock.Any(), + }, + }, + args: args{x: false}, + want: true, + }, + { + name: "multiple elements (first ok, second ko)", + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Any(), + gomock.Eq(true), + }, + }, + args: args{x: false}, + want: true, + }, + { + name: "multiple elements (first ko, second ok)", + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Eq(true), + gomock.Any(), + }, + }, + args: args{x: false}, + want: true, + }, + { + name: "multiple elements (first ko, second ko)", + fields: fields{ + matchers: []gomock.Matcher{ + gomock.Eq(true), + gomock.Eq(true), + }, + }, + args: args{x: false}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + om := &orMatcher{ + matchers: tt.fields.matchers, + } + if got := om.Matches(tt.args.x); got != tt.want { + t.Errorf("orMatcher.Matches() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestOrMatcher(t *testing.T) { + type args struct { + matchers []gomock.Matcher + } + tests := []struct { + name string + args args + want gomock.Matcher + }{ + { + name: "init", + args: args{matchers: []gomock.Matcher{}}, + want: &orMatcher{matchers: []gomock.Matcher{}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := OrMatcher(tt.args.matchers...); !reflect.DeepEqual(got, tt.want) { + t.Errorf("OrMatcher() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/string-regexp-matcher.go b/string-regexp-matcher.go index 6189f19..c20afaa 100644 --- a/string-regexp-matcher.go +++ b/string-regexp-matcher.go @@ -23,7 +23,7 @@ func (s *stringRegexpMatcher) Matches(x interface{}) bool { return false } - return s.reg.Match([]byte(st)) + return s.reg.MatchString(st) } // Will return a new string regexp matcher.