diff --git a/.dockerignore b/.dockerignore index b2db58a..c5fa12a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-11T19:57:58Z by kres latest. +# Generated on 2024-05-19T20:59:37Z by kres dccd292. * !channel diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2dc54b6..4a89d69 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,6 +1,6 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-11T19:57:58Z by kres latest. +# Generated on 2024-05-19T18:27:53Z by kres dccd292. name: default concurrency: @@ -31,7 +31,7 @@ jobs: if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) services: buildkitd: - image: moby/buildkit:v0.12.5 + image: moby/buildkit:v0.13.2 options: --privileged ports: - 1234:1234 @@ -45,11 +45,12 @@ jobs: run: | git fetch --prune --unshallow - name: Set up Docker Buildx + id: setup-buildx uses: docker/setup-buildx-action@v3 with: driver: remote endpoint: tcp://127.0.0.1:1234 - timeout-minutes: 1 + timeout-minutes: 10 - name: base run: | make base @@ -60,8 +61,11 @@ jobs: run: | make unit-tests-race - name: coverage - run: | - make coverage + uses: codecov/codecov-action@v4 + with: + files: _out/coverage-unit-tests.txt + token: ${{ secrets.CODECOV_TOKEN }} + timeout-minutes: 3 - name: lint run: | make lint diff --git a/.golangci.yml b/.golangci.yml index 41af67e..f0035e5 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,21 +1,20 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-11T19:57:58Z by kres latest. +# Generated on 2024-05-19T20:58:19Z by kres dccd292. # options for analysis running run: timeout: 10m issues-exit-code: 1 tests: true - build-tags: [] - skip-dirs: [] - skip-dirs-use-default: true - skip-files: [] + build-tags: [ ] modules-download-mode: readonly # output configuration options output: - format: colored-line-number + formats: + - format: colored-line-number + path: stdout print-issued-lines: true print-linter-name: true uniq-by-line: true @@ -32,54 +31,35 @@ linters-settings: check-blank: true exhaustive: default-signifies-exhaustive: false - funlen: - lines: 60 - statements: 40 gci: - local-prefixes: github.com/siderolabs/gen/ + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - localmodule # Imports from the same module. gocognit: min-complexity: 30 - ireturn: - allow: - - anon - - error - - empty - - stdlib - - github.com\/talos-systems\/kres\/internal\/dag.Node nestif: min-complexity: 5 goconst: min-len: 3 min-occurrences: 3 gocritic: - disabled-checks: [] + disabled-checks: [ ] gocyclo: min-complexity: 20 godot: - check-all: false - godox: - keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting - - NOTE - - OPTIMIZE # marks code that should be optimized before merging - - HACK # marks hack-arounds that should be removed before merging + scope: declarations gofmt: simplify: true - goimports: - local-prefixes: github.com/siderolabs/gen/ - golint: - min-confidence: 0.8 - gomnd: - settings: {} - gomodguard: {} + gomodguard: { } govet: - check-shadowing: true enable-all: true lll: line-length: 200 tab-width: 4 misspell: locale: US - ignore-words: [] + ignore-words: [ ] nakedret: max-func-lines: 30 prealloc: @@ -88,16 +68,15 @@ linters-settings: for-loops: false # Report preallocation suggestions on for loops, false by default nolintlint: allow-unused: false - allow-leading-space: false - allow-no-explanation: [] + allow-no-explanation: [ ] require-explanation: false require-specific: true - rowserrcheck: {} - testpackage: {} + rowserrcheck: { } + testpackage: { } unparam: check-exported: false unused: - check-exported: false + local-variables-are-used: false 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 @@ -113,8 +92,8 @@ linters-settings: gofumpt: extra-rules: false cyclop: - # the maximal code complexity to report - max-complexity: 20 + # the maximal code complexity to report + max-complexity: 20 # depguard: # Main: # deny: @@ -125,48 +104,50 @@ linters: disable-all: false fast: false disable: - - exhaustruct - exhaustivestruct + - exhaustruct + - err113 - forbidigo - funlen - - gas - gochecknoglobals - gochecknoinits - godox - - goerr113 - gomnd - gomoddirectives + - gosec + - inamedparam - ireturn + - mnd - nestif - nonamedreturns - nosnakecase - paralleltest + - tagalign - tagliatelle - thelper - typecheck - varnamelen - wrapcheck - depguard # Disabled because starting with golangci-lint 1.53.0 it doesn't allow denylist alone anymore - - tagalign - - inamedparam - testifylint # complains about our assert recorder and has a number of false positives for assert.Greater(t, thing, 1) - protogetter # complains about us using Value field on typed spec, instead of GetValue which has a different signature - perfsprint # complains about us using fmt.Sprintf in non-performance critical code, updating just kres took too long # abandoned linters for which golangci shows the warning that the repo is archived by the owner + - deadcode + - golint + - ifshort - interfacer - maligned - - golint - scopelint - - varcheck - - deadcode - structcheck - - ifshort + - varcheck # disabled as it seems to be broken - goes into imported libraries and reports issues there - musttag + - goimports # same as gci issues: - exclude: [] - exclude-rules: [] + exclude: [ ] + exclude-rules: [ ] exclude-use-default: false exclude-case-sensitive: false max-issues-per-linter: 10 diff --git a/Dockerfile b/Dockerfile index 1474d75..8ab988e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -# syntax = docker/dockerfile-upstream:1.7.0-labs +# syntax = docker/dockerfile-upstream:1.7.1-labs # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-11T19:57:58Z by kres latest. +# Generated on 2024-05-19T20:59:37Z by kres dccd292. ARG TOOLCHAIN @@ -10,7 +10,7 @@ ARG TOOLCHAIN FROM scratch AS generate # runs markdownlint -FROM docker.io/node:21.6.2-alpine3.19 AS lint-markdown +FROM docker.io/node:21.7.3-alpine3.19 AS lint-markdown WORKDIR /src RUN npm i -g markdownlint-cli@0.39.0 RUN npm i sentences-per-line@0.2.1 @@ -40,9 +40,6 @@ RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/g && mv /go/bin/golangci-lint /bin/golangci-lint RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/vuln/cmd/govulncheck@latest \ && mv /go/bin/govulncheck /bin/govulncheck -ARG GOIMPORTS_VERSION -RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/tools/cmd/goimports@${GOIMPORTS_VERSION} \ - && mv /go/bin/goimports /bin/goimports ARG GOFUMPT_VERSION RUN go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \ && mv /go/bin/gofumpt /bin/gofumpt @@ -71,15 +68,12 @@ RUN --mount=type=cache,target=/go/pkg go list -mod=readonly all >/dev/null FROM base AS lint-gofumpt RUN FILES="$(gofumpt -l .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'gofumpt -w .':\n${FILES}"; exit 1) -# runs goimports -FROM base AS lint-goimports -RUN FILES="$(goimports -l -local github.com/siderolabs/gen/ .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'goimports -w -local github.com/siderolabs/gen/ .':\n${FILES}"; exit 1) - # runs golangci-lint FROM base AS lint-golangci-lint WORKDIR /src COPY .golangci.yml . ENV GOGC 50 +RUN golangci-lint config verify --config .golangci.yml RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/root/.cache/golangci-lint --mount=type=cache,target=/go/pkg golangci-lint run --config .golangci.yml # runs govulncheck diff --git a/Makefile b/Makefile index a60d7d8..b4979d2 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # -# Generated on 2024-03-11T19:51:28Z by kres latest. +# Generated on 2024-05-19T18:27:53Z by kres dccd292. # common variables @@ -9,6 +9,9 @@ TAG := $(shell git describe --tag --always --dirty --match v[0-9]\*) ABBREV_TAG := $(shell git describe --tags >/dev/null 2>/dev/null && git describe --tag --always --match v[0-9]\* --abbrev=0 || echo 'undefined') BRANCH := $(shell git rev-parse --abbrev-ref HEAD) ARTIFACTS := _out +IMAGE_TAG ?= $(TAG) +OPERATING_SYSTEM := $(shell uname -s | tr '[:upper:]' '[:lower:]') +GOARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') WITH_DEBUG ?= false WITH_RACE ?= false REGISTRY ?= ghcr.io @@ -18,11 +21,11 @@ PROTOBUF_GO_VERSION ?= 1.33.0 GRPC_GO_VERSION ?= 1.3.0 GRPC_GATEWAY_VERSION ?= 2.19.1 VTPROTOBUF_VERSION ?= 0.6.0 +GOIMPORTS_VERSION ?= 0.21.0 DEEPCOPY_VERSION ?= v0.5.6 -GOLANGCILINT_VERSION ?= v1.56.2 +GOLANGCILINT_VERSION ?= v1.58.0 GOFUMPT_VERSION ?= v0.6.0 -GO_VERSION ?= 1.22.1 -GOIMPORTS_VERSION ?= v0.19.0 +GO_VERSION ?= 1.22.3 GO_BUILDFLAGS ?= GO_LDFLAGS ?= CGO_ENABLED ?= 0 @@ -59,9 +62,9 @@ COMMON_ARGS += --build-arg=PROTOBUF_GO_VERSION="$(PROTOBUF_GO_VERSION)" COMMON_ARGS += --build-arg=GRPC_GO_VERSION="$(GRPC_GO_VERSION)" COMMON_ARGS += --build-arg=GRPC_GATEWAY_VERSION="$(GRPC_GATEWAY_VERSION)" COMMON_ARGS += --build-arg=VTPROTOBUF_VERSION="$(VTPROTOBUF_VERSION)" +COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)" COMMON_ARGS += --build-arg=DEEPCOPY_VERSION="$(DEEPCOPY_VERSION)" COMMON_ARGS += --build-arg=GOLANGCILINT_VERSION="$(GOLANGCILINT_VERSION)" -COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)" COMMON_ARGS += --build-arg=GOFUMPT_VERSION="$(GOFUMPT_VERSION)" COMMON_ARGS += --build-arg=TESTPKGS="$(TESTPKGS)" TOOLCHAIN ?= docker.io/golang:1.22-alpine @@ -110,7 +113,7 @@ If you already have a compatible builder instance, you may use that instead. ## Artifacts All artifacts will be output to ./$(ARTIFACTS). Images will be tagged with the -registry "$(REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(IMAGE):$(TAG)). +registry "$(REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(IMAGE):$(IMAGE_TAG)). The registry and username can be overridden by exporting REGISTRY, and USERNAME respectively. @@ -130,6 +133,9 @@ endif all: unit-tests lint +$(ARTIFACTS): ## Creates artifacts directory. + @mkdir -p $(ARTIFACTS) + .PHONY: clean clean: ## Cleans up all artifacts. @rm -rf $(ARTIFACTS) @@ -157,9 +163,6 @@ fmt: ## Formats the source code lint-govulncheck: ## Runs govulncheck linter. @$(MAKE) target-$@ -lint-goimports: ## Runs goimports linter. - @$(MAKE) target-$@ - .PHONY: base base: ## Prepare base toolchain @$(MAKE) target-$@ @@ -172,16 +175,12 @@ unit-tests: ## Performs unit tests unit-tests-race: ## Performs unit tests with race detection enabled. @$(MAKE) target-$@ -.PHONY: coverage -coverage: ## Upload coverage data to codecov.io. - bash -c "bash <(curl -s https://codecov.io/bash) -f $(ARTIFACTS)/coverage-unit-tests.txt -X fix" - .PHONY: lint-markdown lint-markdown: ## Runs markdownlint. @$(MAKE) target-$@ .PHONY: lint -lint: lint-golangci-lint lint-gofumpt lint-govulncheck lint-goimports lint-markdown ## Run all linters for the project. +lint: lint-golangci-lint lint-gofumpt lint-govulncheck lint-markdown ## Run all linters for the project. .PHONY: rekres rekres: @@ -194,8 +193,7 @@ help: ## This help menu. @grep -E '^[a-zA-Z%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' .PHONY: release-notes -release-notes: - mkdir -p $(ARTIFACTS) +release-notes: $(ARTIFACTS) @ARTIFACTS=$(ARTIFACTS) ./hack/release.sh $@ $(ARTIFACTS)/RELEASE_NOTES.md $(TAG) .PHONY: conformance diff --git a/containers/map_test.go b/containers/map_test.go index 3e67885..57b8cd4 100644 --- a/containers/map_test.go +++ b/containers/map_test.go @@ -6,7 +6,7 @@ package containers_test import ( "fmt" - "math/rand" + "math/rand/v2" "sync" "testing" @@ -145,8 +145,8 @@ func parallelGetOrCall(t *testing.T, m *containers.ConcurrentMap[int, int], our, oneAnotherGet := false - for i := 0; i < 10000; i++ { - key := int(rand.Int63n(10000)) + for range 10000 { + key := int(rand.Int64N(10000)) res, ok := m.GetOrCall(key, func() int { return key * our }) if ok { @@ -182,8 +182,8 @@ func parallelGetOrCreate(t *testing.T, m *containers.ConcurrentMap[int, int], ou oneAnotherGet := false - for i := 0; i < 10000; i++ { - key := int(rand.Int63n(10000)) + for range 10000 { + key := int(rand.Int64N(10000)) res, ok := m.GetOrCreate(key, key*our) if ok { diff --git a/go.mod b/go.mod index 11d642c..c9b7941 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module github.com/siderolabs/gen -go 1.22.0 // Starting with Go 1.21 you have to provide the third digit too. +// Starting with Go 1.21 you have to provide the third digit too. +go 1.22.0 require github.com/stretchr/testify v1.9.0 diff --git a/maps/maps.go b/maps/maps.go index 6804718..1b36d55 100644 --- a/maps/maps.go +++ b/maps/maps.go @@ -42,22 +42,6 @@ func Map[K comparable, V any, K1 comparable, V1 any](m map[K]V, fn func(K, V) (K return r } -// Keys returns the keys of the map m. -// The keys will be in an indeterminate order. -func Keys[K comparable, V any](m map[K]V) []K { - if len(m) == 0 { - return nil - } - - r := make([]K, 0, len(m)) - - for k := range m { - r = append(r, k) - } - - return r -} - // KeysFunc applies the function fn to each key of the map m and returns a new slice with the results. // The keys will be in an indeterminate order. func KeysFunc[K comparable, V, R any](m map[K]V, fn func(K) R) []R { @@ -74,18 +58,6 @@ func KeysFunc[K comparable, V, R any](m map[K]V, fn func(K) R) []R { return r } -// Values returns the values of the map m. -// The values will be in an indeterminate order. -func Values[K comparable, V any](m map[K]V) []V { - r := make([]V, 0, len(m)) - - for _, v := range m { - r = append(r, v) - } - - return r -} - // ValuesFunc applies the function fn to each value of the map m and returns a new slice with the results. // The values will be in an indeterminate order. func ValuesFunc[K comparable, V, R any](m map[K]V, fn func(V) R) []R { diff --git a/maps/maps_linkname.go b/maps/maps_linkname.go new file mode 100644 index 0000000..7847c19 --- /dev/null +++ b/maps/maps_linkname.go @@ -0,0 +1,46 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//go:build go1.22 && !go1.24 && !nolinkname + +//nolint:revive +package maps + +import "unsafe" + +//go:linkname runtime_keys maps.keys +//go:noescape +func runtime_keys(m any, p unsafe.Pointer) + +// Keys returns the keys of the map m. +// The keys will be in an indeterminate order. +func Keys[K comparable, V any](m map[K]V) []K { + if len(m) == 0 { + return nil + } + + result := make([]K, 0, len(m)) + + runtime_keys(m, unsafe.Pointer(&result)) + + return result +} + +//go:linkname runtime_values maps.values +//go:noescape +func runtime_values(m any, p unsafe.Pointer) + +// Values returns the values of the map m. +// The values will be in an indeterminate order. +func Values[K comparable, V any](m map[K]V) []V { + if len(m) == 0 { + return nil + } + + result := make([]V, 0, len(m)) + + runtime_values(m, unsafe.Pointer(&result)) + + return result +} diff --git a/maps/maps_nolinkname.go b/maps/maps_nolinkname.go new file mode 100644 index 0000000..e2230b0 --- /dev/null +++ b/maps/maps_nolinkname.go @@ -0,0 +1,39 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//go:build go1.24 || nolinkname + +package maps + +// Keys returns the keys of the map m. +// The keys will be in an indeterminate order. +func Keys[K comparable, V any](m map[K]V) []K { + if len(m) == 0 { + return nil + } + + r := make([]K, 0, len(m)) + + for k := range m { + r = append(r, k) + } + + return r +} + +// Values returns the values of the map m. +// The values will be in an indeterminate order. +func Values[K comparable, V any](m map[K]V) []V { + if len(m) == 0 { + return nil + } + + r := make([]V, 0, len(m)) + + for _, v := range m { + r = append(r, v) + } + + return r +} diff --git a/maps/maps_test.go b/maps/maps_test.go index 03ad901..dca2c59 100644 --- a/maps/maps_test.go +++ b/maps/maps_test.go @@ -57,8 +57,6 @@ func TestFilterInPlace(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -113,8 +111,6 @@ func TestFilter(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -163,8 +159,6 @@ func TestKeys(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -214,8 +208,6 @@ func TestKeysFunc(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -265,8 +257,6 @@ func TestToSlice(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -316,8 +306,6 @@ func TestValuesFunc(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -422,8 +410,6 @@ func TestIntersection(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -434,3 +420,83 @@ func TestIntersection(t *testing.T) { }) } } + +func TestKeysAddtional(t *testing.T) { + m := generateMap(6) + + keys := maps.Keys(m) + + assert.Equal(t, 6, len(keys)) + slices.Sort(keys) + assert.EqualValues(t, []int{0, 1, 2, 3, 4, 5}, keys) +} + +func TestValuesAddtional(t *testing.T) { + m := generateMap(6) + + values := maps.Values(m) + + assert.Equal(t, 6, len(values)) + slices.Sort(values) + assert.EqualValues(t, []int{-5, -4, -3, -2, -1, 0}, values) +} + +var Sink []int + +func BenchmarkKeys(b *testing.B) { + smallMap := generateMap(10) + midMap := generateMap(100) + largeMap := generateMap(1000) + + b.Run("small", func(b *testing.B) { + for range b.N { + Sink = maps.Keys(smallMap) + } + }) + + b.Run("mid", func(b *testing.B) { + for range b.N { + Sink = maps.Keys(midMap) + } + }) + + b.Run("large", func(b *testing.B) { + for range b.N { + Sink = maps.Keys(largeMap) + } + }) +} + +func BenchmarkValues(b *testing.B) { + smallMap := generateMap(10) + midMap := generateMap(100) + largeMap := generateMap(1000) + + b.Run("small", func(b *testing.B) { + for range b.N { + Sink = maps.Values(smallMap) + } + }) + + b.Run("mid", func(b *testing.B) { + for range b.N { + Sink = maps.Values(midMap) + } + }) + + b.Run("large", func(b *testing.B) { + for range b.N { + Sink = maps.Values(largeMap) + } + }) +} + +func generateMap(num int) map[int]int { + result := make(map[int]int, num) + + for i := range num { + result[i] = -i + } + + return result +} diff --git a/pair/ordered/ordered_test.go b/pair/ordered/ordered_test.go index 34fd00d..c46601e 100644 --- a/pair/ordered/ordered_test.go +++ b/pair/ordered/ordered_test.go @@ -6,7 +6,7 @@ package ordered_test import ( "math" - "math/rand" + "math/rand/v2" "slices" "testing" "time" @@ -32,14 +32,15 @@ func TestTriple(t *testing.T) { ordered.MakeTriple(math.MaxInt64, "", 69.0), } - seed := time.Now().Unix() - rnd := rand.New(rand.NewSource(seed)) + seed1 := time.Now().UnixNano() + seed2 := time.Now().UnixNano() + rnd := rand.New(rand.NewPCG(uint64(seed1), uint64(seed2))) - for i := 0; i < 1000; i++ { + for i := range 1000 { a := append([]ordered.Triple[int, string, float64](nil), expectedSlice...) rnd.Shuffle(len(a), func(i, j int) { a[i], a[j] = a[j], a[i] }) slices.SortFunc(a, func(i, j ordered.Triple[int, string, float64]) int { return i.Compare(j) }) - require.Equal(t, expectedSlice, a, "failed with seed %d iteration %d", seed, i) + require.Equal(t, expectedSlice, a, "failed with seed1 %d seed2 %d iteration %d", seed1, seed2, i) } } diff --git a/xslices/xslices_test.go b/xslices/xslices_test.go index 38a1f0b..fbd685c 100644 --- a/xslices/xslices_test.go +++ b/xslices/xslices_test.go @@ -68,8 +68,6 @@ func TestFilterInPlace(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel() @@ -138,8 +136,6 @@ func TestFilter(t *testing.T) { } for name, tt := range tests { - tt := tt - t.Run(name, func(t *testing.T) { t.Parallel()