diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 4c70a6c..c4bcf1b 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -6,6 +6,6 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/cache@v5 with: repo-token: "${{ github.token }}" diff --git a/.github/workflows/site.yml b/.github/workflows/site.yml index 94a2f9c..5f8e70e 100644 --- a/.github/workflows/site.yml +++ b/.github/workflows/site.yml @@ -2,6 +2,8 @@ name: 'site' on: push: + branches: + - main paths: - 'site/**' - '.github/workflows/site.yml' @@ -33,7 +35,7 @@ jobs: cd site hugo version hugo --gc --minify - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: site path: site/public diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ab24be4..2695de8 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue is being marked as stale due to a long period of inactivity' diff --git a/.github/workflows/test-completions.yml b/.github/workflows/test-completions.yml index 8010861..d985be8 100644 --- a/.github/workflows/test-completions.yml +++ b/.github/workflows/test-completions.yml @@ -9,29 +9,16 @@ env: GO111MODULE: on jobs: - DetermineVersion: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - id: go-version-matrix - run: | - go_version="$(awk '/^go/{print $2}' go.mod)" - echo "go_version=${go_version}.x" >> $GITHUB_OUTPUT - outputs: - go_version: ${{ steps.go-version-matrix.outputs.go_version }} - Test-Docker: needs: DetermineVersion runs-on: ubuntu-latest name: Docker Tests steps: - - name: Setup go - shell: bash - run: curl -sL https://raw.githubusercontent.com/maxatome/install-go/v3.3/install-go.pl | perl - ${{ needs.DetermineVersion.outputs.go_version }} $HOME - - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - uses: actions/cache@v4 with: path: | ~/.cache/go-build diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 54fa5ce..94a0f3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,8 @@ name: Test on: push: + branches: + - main paths: - '**.go' - '**.gotmpl' @@ -20,46 +22,22 @@ env: GO111MODULE: on jobs: - DetermineVersion: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - id: go-version-matrix - run: | - go_version="$(awk '/^go/{print $2}' go.mod)" - echo "go_version=${go_version}.x" >> $GITHUB_OUTPUT - - matrix_versions="$(git ls-remote --tags https://github.com/golang/go | - awk -F/ '$3 ~ /^go([0-9]+(\.[0-9]+)$)/{gsub(/^go/, "", $3); print $3}' | - sort -V | - sed -ne "/^$go_version$/,$ p" | - jq -cMnR '[inputs | select(length>0)] | map(. + ".x") + ["tip"]')" - echo "matrix=$matrix_versions" >> $GITHUB_OUTPUT - - golangci_version="$(yq '.repos[] | select(.repo | contains("golangci-lint")) | .rev' .pre-commit-config.yaml)" - echo "golangci_version=$golangci_version" >> $GITHUB_OUTPUT - outputs: - matrix: ${{ steps.go-version-matrix.outputs.matrix }} - go_version: ${{ steps.go-version-matrix.outputs.go_version }} - golangci_version: ${{ steps.go-version-matrix.outputs.golangci_version }} - Pre-Commit: - needs: DetermineVersion runs-on: ubuntu-latest steps: - - uses: actions/setup-python@v4 - - name: Setup go - run: curl -sL https://raw.githubusercontent.com/maxatome/install-go/v3.3/install-go.pl | perl - ${{ needs.DetermineVersion.outputs.go_version }} $HOME - uses: actions/checkout@v4 - - uses: actions/cache@v3 + - uses: actions/setup-python@v5 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/Library/Caches/go-build ~/AppData/Local/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ needs.DetermineVersion.outputs.go_version }}-${{ hashFiles('**/go.sum') }} + key: go-${{ hashFiles('**/go.sum') }} restore-keys: go-mod - name: Setup mdtoc run: go install sigs.k8s.io/mdtoc@latest @@ -68,63 +46,58 @@ jobs: SKIP: golangci-lint,go-build,go-unit-tests GolangCI-Lint: - needs: DetermineVersion runs-on: ubuntu-latest steps: - - name: Setup go - run: curl -sL https://raw.githubusercontent.com/maxatome/install-go/v3.3/install-go.pl | perl - ${{ needs.DetermineVersion.outputs.go_version }} $HOME - - name: Checkout code - uses: actions/checkout@v4 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - id: versions + run: | + golangci="$(yq '.repos[] | select(.repo | contains("golangci-lint")) | .rev' .pre-commit-config.yaml)" + echo "golangci=$golangci" >> "$GITHUB_OUTPUT" + - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/Library/Caches/go-build ~/AppData/Local/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ needs.DetermineVersion.outputs.go_version }}-${{ hashFiles('**/go.sum') }} + key: go-${{ hashFiles('**/go.sum') }} restore-keys: go-mod - name: Run linters - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: - version: ${{ needs.DetermineVersion.outputs.golangci_version }} + version: ${{ steps.versions.outputs.golangci }} Vet: - needs: DetermineVersion runs-on: ubuntu-latest steps: - - name: Setup go - run: curl -sL https://raw.githubusercontent.com/maxatome/install-go/v3.3/install-go.pl | perl - ${{ needs.DetermineVersion.outputs.go_version }} $HOME - - name: Checkout code - uses: actions/checkout@v4 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - uses: actions/cache@v4 with: path: | ~/.cache/go-build ~/Library/Caches/go-build ~/AppData/Local/go-build ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ needs.DetermineVersion.outputs.go_version }}-${{ hashFiles('**/go.sum') }} + key: go-${{ hashFiles('**/go.sum') }} restore-keys: go-mod - name: Run vet run: go vet Test: - needs: DetermineVersion - strategy: - fail-fast: false - matrix: - go: ${{ fromJson(needs.DetermineVersion.outputs.matrix) }} - os: [ ubuntu-latest, macOS-latest, windows-latest ] - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} Go ${{ matrix.go }} Tests + runs-on: ubuntu-latest + name: Go Tests steps: - - name: Setup go - shell: bash - run: curl -sL https://raw.githubusercontent.com/maxatome/install-go/v3.3/install-go.pl | perl - ${{ matrix.go }} $HOME - - name: Checkout code - uses: actions/checkout@v4 - - uses: actions/cache@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - uses: actions/cache@v4 with: path: | ~/.cache/go-build diff --git a/.golangci.yml b/.golangci.yml index 707aa85..077b257 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,42 +1,374 @@ +# This code is licensed under the terms of the MIT license https://opensource.org/license/mit +# Copyright (c) 2021-2025 Marat Reymers +# Based on https://gist.github.com/maratori/47a4d00457a92aa426dbd48a18776322 run: - deadline: 5m + timeout: 3m + +# This file contains only configs which differ from defaults. +# All possible options can be found here https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml +linters-settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 30 + # The maximal average package complexity. + # If it's higher than 0.0 (float) the check is enabled + # Default: 0.0 + package-average: 10.0 + + errcheck: + # Report about not checking of errors in type assertions: `a := b.(MyStruct)`. + # Such cases aren't reported by default. + # Default: false + check-type-assertions: true + + exhaustive: + # Program elements to check for exhaustiveness. + # Default: [ switch ] + check: + - switch + - map + + exhaustruct: + # List of regular expressions to exclude struct packages and their names from checks. + # Regular expressions must match complete canonical struct package/name/structname. + # Default: [] + exclude: + # std libs + - "^net/http.Client$" + - "^net/http.Cookie$" + - "^net/http.Request$" + - "^net/http.Response$" + - "^net/http.Server$" + - "^net/http.Transport$" + - "^net/url.URL$" + - "^os/exec.Cmd$" + - "^reflect.StructField$" + # public libs + - "^github.com/Shopify/sarama.Config$" + - "^github.com/Shopify/sarama.ProducerMessage$" + - "^github.com/mitchellh/mapstructure.DecoderConfig$" + - "^github.com/prometheus/client_golang/.+Opts$" + - "^github.com/spf13/cobra.Command$" + - "^github.com/spf13/cobra.CompletionOptions$" + - "^github.com/stretchr/testify/mock.Mock$" + - "^github.com/testcontainers/testcontainers-go.+Request$" + - "^github.com/testcontainers/testcontainers-go.FromDockerfile$" + - "^golang.org/x/tools/go/analysis.Analyzer$" + - "^google.golang.org/protobuf/.+Options$" + - "^gopkg.in/yaml.v3.Node$" + + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 100 + # Checks the number of statements in a function. + # If lower than 0, disable the check. + # Default: 40 + statements: 50 + # Ignore comments when counting lines. + # Default false + ignore-comments: true + + gochecksumtype: + # Presence of `default` case in switch statements satisfies exhaustiveness, if all members are not listed. + # Default: true + default-signifies-exhaustive: false + + gocognit: + # Minimal code complexity to report. + # Default: 30 (but we recommend 10-20) + min-complexity: 20 + + gocritic: + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: + # Whether to restrict checker to params only. + # Default: true + paramsOnly: false + underef: + # Whether to skip (*x).method() calls where x is a pointer receiver. + # Default: true + skipRecvDeref: false + + gomodguard: + blocked: + # List of blocked modules. + # Default: [] + modules: + - github.com/zulucmd/zulu/v2/internal/testutil: + reason: "Do not use testutil unless it's a test package" + + depguard: + # Rules to apply. + # + # Variables: + # - File Variables + # you can still use and exclamation mark ! in front of a variable to say not to use it. + # Example !$test will match any file that is not a go test file. + # + # `$all` - matches all go files + # `$test` - matches all go test files + # + # - Package Variables + # + # `$gostd` - matches all of go's standard library (Pulled from `GOROOT`) + # + # Default: Only allow $gostd in all files. + rules: + test_pkg: + # Used to determine the package matching priority. + # There are three different modes: `original`, `strict`, and `lax`. + # Default: "strict" + list-mode: lax + # List of file globs that will match this list of settings to compare against. + # Default: $all + files: + - "!$test" + deny: + - pkg: github.com/zulucmd/zulu/v2/internal/testutil + desc: package only allowed in tests + + govet: + # Enable all analyzers. + # Default: false + enable-all: true + # Disable analyzers by name. + # Run `go tool vet help` to see all analyzers. + # Default: [] + disable: + - fieldalignment # too strict + # Settings per analyzer. + + inamedparam: + # Skips check for interface methods with only a single parameter. + # Default: false + skip-single-param: true + + nakedret: + # Make an issue if func has more lines of code than this setting, and it has naked returns. + # Default: 30 + max-func-lines: 0 + + nolintlint: + # Exclude following linters from requiring an explanation. + # Default: [] + allow-no-explanation: [ funlen, gocognit, lll ] + # Enable to require an explanation of nonzero length after each nolint directive. + # Default: false + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. + # Default: false + require-specific: true + + perfsprint: + # Optimizes into strings concatenation. + # Default: true + strconcat: false + + reassign: + # Patterns for global variable names that are checked for reassignment. + # See https://github.com/curioswitch/go-reassign#usage + # Default: ["EOF", "Err.*"] + patterns: + - ".*" + + sloglint: + # Enforce not using global loggers. + # Values: + # - "": disabled + # - "all": report all global loggers + # - "default": report only the default slog logger + # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global + # Default: "" + no-global: "all" + # Enforce using methods that accept a context. + # Values: + # - "": disabled + # - "all": report all contextless calls + # - "scope": report only if a context exists in the scope of the outermost function + # https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only + # Default: "" + context: "scope" + + forbidigo: + # Forbid the following identifiers (list of regexp). + forbid: + - p: "^(fmt\\.Print(|f|ln)|print|println)$" + + tenv: + # The option `all` will run against whole test files (`_test.go`) regardless of method/function signatures. + # Otherwise, only methods that take `*testing.T`, `*testing.B`, and `testing.TB` as arguments are checked. + # Default: false + all: true + + revive: # See https://github.com/mgechev/revive + rules: + - name: unused-parameter + disabled: true + - name: redefines-builtin-id + disabled: true + - name: flag-parameter + disabled: true + - name: add-constant + disabled: true + - name: cyclomatic + disabled: true + - name: modifies-parameter + disabled: true + - name: nested-structs + disabled: true + - name: bare-return + disabled: true linters: disable-all: true enable: - #- bodyclose - - unused - #- depguard - #- dogsled - #- dupl - - errcheck - #- exhaustive - #- funlen - - gas - #- gochecknoinits - #- goconst - #- gocritic - #- gocyclo - #- gofmt - - goimports - #- gomnd - #- goprintffuncname - #- gosec - #- gosimple - - govet - - ineffassign - #- lll - - megacheck - #- misspell - #- nakedret - #- noctx - #- nolintlint - #- rowserrcheck - #- scopelint - - staticcheck - #- stylecheck - #- typecheck - - unconvert - #- unparam - #- whitespace - fast: false + ## enabled by default + - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases + - gosimple # specializes in simplifying a code + - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string + - ineffassign # detects when assignments to existing variables are not used + - staticcheck # is a go vet on steroids, applying a ton of static analysis checks + - typecheck # like the front-end of a Go compiler, parses and type-checks Go code + - unused # checks for unused constants, variables, functions and types + ## disabled by default + - asasalint # checks for pass []any as any in variadic func(...any) + - asciicheck # checks that your code does not contain non-ASCII identifiers + - bidichk # checks for dangerous unicode character sequences + - bodyclose # checks whether HTTP response body is closed successfully + - canonicalheader # checks whether net/http.Header uses canonical header + - copyloopvar # detects places where loop variables are copied (Go 1.22+) + - cyclop # checks function and package cyclomatic complexity + - dupl # tool for code clone detection + - durationcheck # checks for two durations multiplied together + - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error + - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 + - exhaustive # checks exhaustiveness of enum switch statements + - exptostd # detects functions from golang.org/x/exp/ that can be replaced by std functions + - fatcontext # detects nested contexts in loops + - forbidigo # forbids identifiers + - funlen # tool for detection of long functions + - gocheckcompilerdirectives # validates go compiler directive comments (//go:) + # - gochecknoglobals # checks that no global variables exist, disabled for now as cobra was heavy on global variables + # - gochecknoinits # checks that no init functions are present in Go code + - gochecksumtype # checks exhaustiveness on Go "sum types" + - gocognit # computes and checks the cognitive complexity of functions + - goconst # finds repeated strings that could be replaced by a constant + - gocritic # provides diagnostics that check for bugs, performance and style issues + - gocyclo # computes and checks the cyclomatic complexity of functions + - godot # checks if comments end in a period + - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt + - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod + - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations + - depguard # [replaced by gomodguard] checks if package imports are in a list of acceptable packages + - goprintffuncname # checks that printf-like functions are named with f at the end + - gosec # inspects source code for security problems + - iface # checks the incorrect use of interfaces, helping developers avoid interface pollution + - intrange # finds places where for loops could make use of an integer range + - lll # reports long lines + - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) + - makezero # finds slice declarations with non-zero initial length + - mirror # reports wrong mirror patterns of bytes/strings usage + # - mnd # detects magic numbers + - musttag # enforces field tags in (un)marshaled structs + - nakedret # finds naked returns in functions greater than a specified function length + - nestif # reports deeply nested if statements + - nilerr # finds the code that returns nil even if it checks that the error is not nil + - nilnesserr # reports that it checks for err != nil, but it returns a different nil value error (powered by nilness and nilerr) + - nilnil # checks that there is no simultaneous return of nil error and an invalid value + - noctx # finds sending http request without context.Context + - nolintlint # reports ill-formed or insufficient nolint directives + # - nonamedreturns # reports all named returns + - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL + - perfsprint # checks that fmt.Sprintf can be replaced with a faster alternative + - predeclared # finds code that shadows one of Go's predeclared identifiers + - promlinter # checks Prometheus metrics naming via promlint + - protogetter # reports direct reads from proto message fields when getters should be used + - reassign # checks that package variables are not reassigned + - recvcheck # checks for receiver type consistency + - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint + - rowserrcheck # checks whether Err of rows is checked successfully + - sloglint # ensure consistent code style when using log/slog + - spancheck # checks for mistakes with OpenTelemetry/Census spans + - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed + - stylecheck # is a replacement for golint + - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 + - testableexamples # checks if examples are testable (have an expected output) + - testifylint # checks usage of github.com/stretchr/testify + - testpackage # makes you use a separate _test package + - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes + - unconvert # removes unnecessary type conversions + - unparam # reports unused function parameters + - usestdlibvars # detects the possibility to use variables/constants from the Go standard library + - usetesting # reports uses of functions with replacement inside the testing package + - wastedassign # finds wasted assignment statements + - whitespace # detects leading and trailing whitespace + + ## you may want to enable + - decorder # checks declaration order and count of types, constants, variables and functions + #- exhaustruct # [highly recommend to enable] checks if all structure fields are initialized + - gci # controls golang package import order and makes it always deterministic + - ginkgolinter # [if you use ginkgo/gomega] enforces standards of using ginkgo and gomega + #- godox # detects FIXME, TODO and other comment keywords + - goheader # checks is file header matches to pattern + - inamedparam # [great idea, but too strict, need to ignore a lot of cases by default] reports interfaces with unnamed method parameters + - interfacebloat # checks the number of methods inside an interface + - ireturn # accept interfaces, return concrete types + #- prealloc # [premature optimization, but can be used in some cases] finds slice declarations that could potentially be preallocated + - tagalign # checks that struct tags are well aligned + #- varnamelen # [great idea, but too many false positives] checks that the length of a variable's name matches its scope + #- wrapcheck # checks that errors returned from external packages are wrapped + - zerologlint # detects the wrong usage of zerolog that a user forgets to dispatch zerolog.Event + + ## disabled + #- containedctx # detects struct contained context.Context field + #- contextcheck # [too many false positives] checks the function whether use a non-inherited context + #- dogsled # checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) + #- dupword # [useless without config] checks for duplicate words in the source code + #- err113 # [too strict] checks the errors handling expressions + #- errchkjson # [don't see profit + I'm against of omitting errors like in the first example https://github.com/breml/errchkjson] checks types passed to the json encoding functions. Reports unsupported types and optionally reports occasions, where the check for the returned error can be omitted + #- exportloopref # [not necessary from Go 1.22] checks for pointers to enclosing loop variables + #- forcetypeassert # [replaced by errcheck] finds forced type assertions + #- gofmt # [replaced by goimports] checks whether code was gofmt-ed + #- gofumpt # [replaced by goimports, gofumports is not available yet] checks whether code was gofumpt-ed + #- gosmopolitan # reports certain i18n/l10n anti-patterns in your Go codebase + #- grouper # analyzes expression groups + #- importas # enforces consistent import aliases + #- maintidx # measures the maintainability index of each function + #- misspell # [useless] finds commonly misspelled English words in comments + #- nlreturn # [too strict and mostly code is not more readable] checks for a new line before return and branch statements to increase code clarity + #- paralleltest # [too many false positives] detects missing usage of t.Parallel() method in your Go test + #- tagliatelle # checks the struct tags + #- thelper # detects golang test helpers without t.Helper() call and checks the consistency of test helpers + #- wsl # [too strict and mostly code is not more readable] whitespace linter forces you to use empty lines + + +issues: + # Maximum count of issues with the same text. + # Set to 0 to disable. + # Default: 3 + max-same-issues: 50 + + exclude-rules: + - source: "(noinspection|TODO)" + linters: [ godot ] + - source: "//noinspection" + linters: [ gocritic ] + - path: "_test\\.go" + linters: + - bodyclose + - dupl + - errcheck + - funlen + - goconst + - gosec + - noctx + - wrapcheck + - reassign + - unparam + - gomodguard diff --git a/args.go b/args.go index 68e8743..77132bd 100644 --- a/args.go +++ b/args.go @@ -7,8 +7,7 @@ import ( type PositionalArgs func(cmd *Command, args []string) error -// validateArgs returns an error if there are any positional args that are not in -// the `ValidArgs` field of `Command` +// the `ValidArgs` field of `Command`. func validateArgs(cmd *Command, args []string) error { if len(cmd.ValidArgs) > 0 { // Remove any description that may be included in ValidArgs. @@ -26,10 +25,7 @@ func validateArgs(cmd *Command, args []string) error { return nil } -// legacyArgs validation has the following behaviour: -// - root commands with no subcommands can take arbitrary arguments -// - root commands with subcommands will do subcommand validity checking -// - subcommands will always accept arbitrary arguments +// - subcommands will always accept arbitrary arguments. func legacyArgs(cmd *Command, args []string) error { // no subcommand, always take args if !cmd.HasSubCommands() { @@ -37,7 +33,7 @@ func legacyArgs(cmd *Command, args []string) error { } // root command with subcommands, do subcommand checking. - if !cmd.HasParent() && len(args) > 0 { + if len(args) > 0 && !cmd.HasParent() { return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0])) } return nil @@ -87,6 +83,8 @@ func ExactArgs(n int) PositionalArgs { } // RangeArgs returns an error if the number of args is not within the expected range. +// +//nolint:predeclared // no real alternative names for min func RangeArgs(min int, max int) PositionalArgs { return func(cmd *Command, args []string) error { if len(args) < min || len(args) > max { diff --git a/args_test.go b/args_test.go index 4d5dbf2..c61e445 100644 --- a/args_test.go +++ b/args_test.go @@ -1,10 +1,11 @@ package zulu_test import ( - "fmt" + "errors" "testing" "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestArgs(t *testing.T) { @@ -66,7 +67,6 @@ func TestArgs(t *testing.T) { t.Parallel() for name, tc := range tests { - tc := tc t.Run(name, func(t *testing.T) { t.Parallel() @@ -88,14 +88,14 @@ func TestArgs(t *testing.T) { output, err := executeCommand(c, tc.rargs...) if len(tc.exerr) > 0 { - assertNotNilf(t, err, "Expected error") - assertEqual(t, expected, err.Error()) + testutil.AssertNotNilf(t, err, "Expected error") + testutil.AssertEqual(t, expected, err.Error()) return } // Expect success - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") }, ) } @@ -109,8 +109,8 @@ func TestRootTakesNoArgs(t *testing.T) { rootCmd.AddCommand(childCmd) _, err := executeCommand(rootCmd, "illegal", "args") - assertNotNilf(t, err, "Expected an error") - assertContains(t, `unknown command "illegal" for "root"`, err.Error()) + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, `unknown command "illegal" for "root"`, err.Error()) } func TestRootTakesArgs(t *testing.T) { @@ -119,7 +119,7 @@ func TestRootTakesArgs(t *testing.T) { rootCmd.AddCommand(childCmd) _, err := executeCommand(rootCmd, "legal", "args") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestChildTakesNoArgs(t *testing.T) { @@ -128,8 +128,8 @@ func TestChildTakesNoArgs(t *testing.T) { rootCmd.AddCommand(childCmd) _, err := executeCommand(rootCmd, "child", "illegal", "args") - assertNotNilf(t, err, "Expected an error") - assertContains(t, `unknown command "illegal" for "root child"`, err.Error()) + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, `unknown command "illegal" for "root child"`, err.Error()) } func TestChildTakesArgs(t *testing.T) { @@ -138,7 +138,7 @@ func TestChildTakesArgs(t *testing.T) { rootCmd.AddCommand(childCmd) _, err := executeCommand(rootCmd, "child", "legal", "args") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestMatchAll(t *testing.T) { @@ -149,7 +149,7 @@ func TestMatchAll(t *testing.T) { func(cmd *zulu.Command, args []string) error { for _, arg := range args { if len([]byte(arg)) != 2 { - return fmt.Errorf("expected to be exactly 2 bytes long") + return errors.New("expected to be exactly 2 bytes long") } } return nil @@ -180,9 +180,9 @@ func TestMatchAll(t *testing.T) { t.Run(name, func(t *testing.T) { _, err := executeCommand(rootCmd, tc.args...) if !tc.fail { - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } else { - assertNotNilf(t, err, "Expected an error") + testutil.AssertNotNilf(t, err, "Expected an error") } }) } @@ -196,12 +196,10 @@ func TestLegacyArgsRootAcceptsArgs(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Args: nil, RunE: noopRun} _, err := executeCommand(rootCmd, "somearg") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } -// This test make sure we keep backwards-compatibility with respect -// to the legacyArgs() function. -// It makes sure a sub-command accepts arguments and further sub-commands +// It makes sure a sub-command accepts arguments and further sub-commands. func TestLegacyArgsSubcmdAcceptsArgs(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Args: nil, RunE: noopRun} childCmd := &zulu.Command{Use: "child", Args: nil, RunE: noopRun} @@ -210,5 +208,5 @@ func TestLegacyArgsSubcmdAcceptsArgs(t *testing.T) { childCmd.AddCommand(grandchildCmd) _, err := executeCommand(rootCmd, "child", "somearg") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } diff --git a/bash_completions.go b/bash_completions.go index c9e30cb..3deccf2 100644 --- a/bash_completions.go +++ b/bash_completions.go @@ -1,7 +1,6 @@ package zulu import ( - _ "embed" "io" "os" diff --git a/bash_completions_test.go b/bash_completions_test.go index d5cd146..dde2256 100644 --- a/bash_completions_test.go +++ b/bash_completions_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestCompleteNoDesCmdInBashScript(t *testing.T) { @@ -18,10 +19,10 @@ func TestCompleteNoDesCmdInBashScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenBashCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenBashCompletion(buf, false)) output := buf.String() - assertContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompNoDescRequestCmd) } func TestCompleteCmdInBashScript(t *testing.T) { @@ -34,41 +35,41 @@ func TestCompleteCmdInBashScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenBashCompletion(buf, true)) + testutil.AssertNil(t, rootCmd.GenBashCompletion(buf, true)) output := buf.String() - assertContains(t, output, zulu.ShellCompRequestCmd+"$") - assertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompRequestCmd+"$") + testutil.AssertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) } func TestBashProgWithDash(t *testing.T) { rootCmd := &zulu.Command{Use: "root-dash", Args: zulu.NoArgs, RunE: noopRun} buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenBashCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenBashCompletion(buf, false)) output := buf.String() // Functions name should have replace the '-' - assertContains(t, output, "__root_dash_init_completion") - assertNotContains(t, output, "__root-dash_init_completion") + testutil.AssertContains(t, output, "__root_dash_init_completion") + testutil.AssertNotContains(t, output, "__root-dash_init_completion") // The command name should not have replaced the '-' - assertContains(t, output, "__start_root_dash root-dash") - assertNotContains(t, output, "dash root_dash") + testutil.AssertContains(t, output, "__start_root_dash root-dash") + testutil.AssertNotContains(t, output, "dash root_dash") } func TestBashProgWithColon(t *testing.T) { rootCmd := &zulu.Command{Use: "root:colon", Args: zulu.NoArgs, RunE: noopRun} buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenBashCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenBashCompletion(buf, false)) output := buf.String() // Functions name should have replace the ':' - assertContains(t, output, "__root_colon_init_completion") - assertNotContains(t, output, "__root:colon_init_completion") + testutil.AssertContains(t, output, "__root_colon_init_completion") + testutil.AssertNotContains(t, output, "__root:colon_init_completion") // The command name should not have replaced the ':' - assertContains(t, output, "__start_root_colon root:colon") - assertNotContains(t, output, "colon root_colon") + testutil.AssertContains(t, output, "__start_root_colon root:colon") + testutil.AssertNotContains(t, output, "colon root_colon") } func TestGenBashCompletionFile(t *testing.T) { @@ -87,7 +88,7 @@ func TestGenBashCompletionFile(t *testing.T) { } rootCmd.AddCommand(child) - assertNil(t, rootCmd.GenBashCompletionFile("./tmp/test", false)) + testutil.AssertNil(t, rootCmd.GenBashCompletionFile("./tmp/test", false)) } func TestFailGenBashCompletionFile(t *testing.T) { @@ -110,6 +111,6 @@ func TestFailGenBashCompletionFile(t *testing.T) { rootCmd.AddCommand(child) err = rootCmd.GenBashCompletionFile("./tmp/test", false) - assertErrf(t, err, "should raise permission denied error") - assertEqual(t, expectedPermissionError, err.Error()) + testutil.AssertErrf(t, err, "should raise permission denied error") + testutil.AssertEqual(t, expectedPermissionError, err.Error()) } diff --git a/command.go b/command.go index 4a052cb..4c65038 100644 --- a/command.go +++ b/command.go @@ -19,7 +19,6 @@ import ( "bytes" "context" "embed" - _ "embed" "errors" "fmt" "io" @@ -37,7 +36,7 @@ const FlagSetByZuluAnnotation = "zulu_annotation_flag_set_by_zulu" //go:embed templates/* var tmplFS embed.FS -// FParseErrAllowList configures Flag parse errors to be ignored +// FParseErrAllowList configures Flag parse errors to be ignored. type FParseErrAllowList zflag.ParseErrorsAllowList // ErrVersion is the error returned if the flag -version is invoked. @@ -292,8 +291,7 @@ func (c *Command) Context() context.Context { return c.ctx } -// SetContext sets context for the command. It is set to context.Background by default and will be overwritten by -// Command.ExecuteContext or Command.ExecuteContextC +// Command.ExecuteContext or Command.ExecuteContextC. func (c *Command) SetContext(ctx context.Context) { c.ctx = ctx } @@ -384,17 +382,17 @@ func (c *Command) OutOrStdout() io.Writer { return c.getOut(os.Stdout) } -// OutOrStderr returns output to stderr +// OutOrStderr returns output to stderr. func (c *Command) OutOrStderr() io.Writer { return c.getOut(os.Stderr) } -// ErrOrStderr returns output to stderr +// ErrOrStderr returns output to stderr. func (c *Command) ErrOrStderr() io.Writer { return c.getErr(os.Stderr) } -// InOrStdin returns input to stdin +// InOrStdin returns input to stdin. func (c *Command) InOrStdin() io.Reader { return c.getIn(os.Stdin) } @@ -431,7 +429,7 @@ func (c *Command) getIn(def io.Reader) io.Reader { // UsageFunc returns either the function set by SetUsageFunc for this command // or a parent, or it returns a default usage function. -func (c *Command) UsageFunc() (f func(*Command) error) { +func (c *Command) UsageFunc() func(*Command) error { if c.usageFunc != nil { return c.usageFunc } @@ -502,7 +500,7 @@ func (c *Command) UsageString() string { return bb.String() } -// UsageHintString returns a string that describes how to obtain usage instructions +// UsageHintString returns a string that describes how to obtain usage instructions. func (c *Command) UsageHintString() string { return fmt.Sprintf("Run '%v --help' for usage.\n", c.CommandPath()) } @@ -510,7 +508,7 @@ func (c *Command) UsageHintString() string { // FlagErrorFunc returns either the function set by SetFlagErrorFunc for this // command or a parent, or it returns a function which returns the original // error. -func (c *Command) FlagErrorFunc() (f func(*Command, error) error) { +func (c *Command) FlagErrorFunc() func(*Command, error) error { if c.flagErrorFunc != nil { return c.flagErrorFunc } @@ -523,6 +521,12 @@ func (c *Command) FlagErrorFunc() (f func(*Command, error) error) { } } +const ( + minUsagePadding = 25 + minCommandPathPadding = 11 + minNamePadding = 11 +) + type padding struct { Usage int CommandPath int @@ -562,12 +566,6 @@ func (c *Command) Padding() padding { return p } -var minUsagePadding = 25 - -var minCommandPathPadding = 11 - -var minNamePadding = 11 - // UsageTemplate returns usage template for the command. func (c *Command) UsageTemplate() string { if c.usageTemplate != "" { @@ -795,7 +793,8 @@ func (c *Command) Traverse(args []string) (*Command, []string, error) { flags = append(flags, arg) continue // A short flag with a space separated value - case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && len(arg) == 2 && !isShortBoolFlag(arg[1:], c.Flags()): + case strings.HasPrefix(arg, "-") && !strings.Contains(arg, "=") && + len(arg) == 2 && !isShortBoolFlag(arg[1:], c.Flags()): inFlag = true flags = append(flags, arg) continue @@ -828,7 +827,7 @@ func (c *Command) SuggestionsFor(typedName string) []string { var suggestions []string for _, cmd := range c.commands { if cmd.IsAvailableCommand() { - levenshteinDistance := ld(typedName, cmd.Name(), true) + levenshteinDistance := calculateLevenshteinDistance(typedName, cmd.Name(), true) suggestByLevenshtein := levenshteinDistance <= c.SuggestionsMinimumDistance suggestByPrefix := strings.HasPrefix(strings.ToLower(cmd.Name()), strings.ToLower(typedName)) if suggestByLevenshtein || suggestByPrefix { @@ -872,9 +871,10 @@ func (c *Command) CancelRun() { c.RunE = nil } +//nolint:gocognit,funlen // to be broken down later func (c *Command) execute(a []string) (err error) { if c == nil { - return fmt.Errorf("called Execute() on a nil Command") + return errors.New("called Execute() on a nil Command") } if len(c.Deprecated) > 0 { @@ -887,14 +887,14 @@ func (c *Command) execute(a []string) (err error) { var hooks []HookFuncE defer func() { - var hooks []HookFuncE - appendHooks(&hooks, c.FinalizeE, c.finalizeHooks) + var finalizeHooks []HookFuncE + appendHooks(&finalizeHooks, c.FinalizeE, c.finalizeHooks) for p := c; p != nil; p = p.Parent() { - appendHooks(&hooks, p.PersistentFinalizeE, p.persistentFinalizeHooks) + appendHooks(&finalizeHooks, p.PersistentFinalizeE, p.persistentFinalizeHooks) } - for _, x := range hooks { - if err := x(c, argWoFlags); err != nil { + for _, x := range finalizeHooks { + if err = x(c, argWoFlags); err != nil { panic(err) } } @@ -915,7 +915,7 @@ func (c *Command) execute(a []string) (err error) { }) hooks = append(hooks, func(cmd *Command, args []string) error { - err := c.ParseFlags(a) + err = c.ParseFlags(a) if err != nil { return c.FlagErrorFunc()(c, err) } @@ -1036,7 +1036,7 @@ func (c *Command) OnPersistentInitialize(f ...HookFuncE) { // OnInitialize registers one or more hooks on the command to be executed // before the flags of the command are parsed. func (c *Command) OnInitialize(f ...HookFuncE) { - c.initializeHooks = append(c.persistentInitializeHooks, f...) + c.initializeHooks = append(c.initializeHooks, f...) } // OnPersistentPreRun registers one or more hooks on the command to be executed @@ -1103,6 +1103,8 @@ func (c *Command) ExecuteContextC(ctx context.Context) (*Command, error) { } // ExecuteC executes the command. +// +//nolint:gocognit // todo later func (c *Command) ExecuteC() (cmd *Command, err error) { if c.ctx == nil { c.ctx = context.Background() @@ -1114,9 +1116,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { } // windows hook - if preExecHookFn != nil { - preExecHookFn(c) - } + runMouseTrap(c) // initialize help at the last point to allow for user overriding c.InitDefaultHelpCmd() @@ -1146,7 +1146,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { } if !c.SilenceErrors { c.PrintErrln("Error:", err.Error()) - c.PrintErrf(cmd.UsageHintString()) + c.PrintErrf("%s", cmd.UsageHintString()) } return c, err } @@ -1159,10 +1159,10 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { cmd.ctx = c.ctx err = cmd.execute(flags) - if err != nil { + if err != nil { //nolint:nestif // todo refactor later // Exit without errors when version requested. At this point the // version has already been printed. - if err == ErrVersion { + if errors.Is(err, ErrVersion) { return cmd, nil } @@ -1185,7 +1185,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { c.Println(cmd.UsageString()) } else if !cmd.SilenceErrors && !c.SilenceErrors { // if SilenceUsage && !SilenceErrors, we should be consistent with the unknown sub-command case and output a hint - c.Printf(cmd.UsageHintString()) + c.Print(cmd.UsageHintString()) } } return cmd, err @@ -1218,7 +1218,13 @@ func (c *Command) InitDefaultHelpFlag() { } else { usage += c.Name() } - c.Flags().Bool("help", false, usage, zflag.OptShorthand('h'), zflag.OptAnnotation(FlagSetByZuluAnnotation, []string{"true"})) + c.Flags().Bool( + "help", + false, + usage, + zflag.OptShorthand('h'), + zflag.OptAnnotation(FlagSetByZuluAnnotation, []string{"true"}), + ) } } @@ -1253,11 +1259,14 @@ func (c *Command) InitDefaultVersionFlag() { // InitDefaultHelpCmd adds default help command to c. // It is called automatically by executing the c or by calling help and usage. // If c already has help command or c has no subcommands, it will do nothing. +// +//nolint:gocognit // todo later func (c *Command) InitDefaultHelpCmd() { if !c.HasSubCommands() { return } + //nolint:nestif // todo later if c.helpCommand == nil { c.helpCommand = &Command{ Use: "help [command]", @@ -1302,7 +1311,7 @@ Simply type ` + c.Name() + ` help [path to command] for full details.`, c.AddCommand(c.helpCommand) } -// ResetCommands delete parent, subcommand and help command from c. +// ResetCommands deletes the parent, subcommand, and help command from c. func (c *Command) ResetCommands() { c.parent = nil c.commands = nil @@ -1385,32 +1394,32 @@ main: } // Print is a convenience method to Print to the defined output, fallback to Stderr if not set. -func (c *Command) Print(i ...interface{}) { +func (c *Command) Print(i ...any) { fmt.Fprint(c.OutOrStderr(), i...) } // Println is a convenience method to Println to the defined output, fallback to Stderr if not set. -func (c *Command) Println(i ...interface{}) { +func (c *Command) Println(i ...any) { c.Print(fmt.Sprintln(i...)) } // Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set. -func (c *Command) Printf(format string, i ...interface{}) { +func (c *Command) Printf(format string, i ...any) { c.Print(fmt.Sprintf(format, i...)) } // PrintErr is a convenience method to Print to the defined Err output, fallback to Stderr if not set. -func (c *Command) PrintErr(i ...interface{}) { +func (c *Command) PrintErr(i ...any) { fmt.Fprint(c.ErrOrStderr(), i...) } // PrintErrln is a convenience method to Println to the defined Err output, fallback to Stderr if not set. -func (c *Command) PrintErrln(i ...interface{}) { +func (c *Command) PrintErrln(i ...any) { c.PrintErr(fmt.Sprintln(i...)) } // PrintErrf is a convenience method to Printf to the defined Err output, fallback to Stderr if not set. -func (c *Command) PrintErrf(format string, i ...interface{}) { +func (c *Command) PrintErrf(format string, i ...any) { c.PrintErr(fmt.Sprintf(format, i...)) } @@ -1441,6 +1450,8 @@ func (c *Command) UseLine() string { // DebugFlags used to determine which flags have been assigned to which commands // and which persist. +// +//nolint:gocognit // todo later func (c *Command) DebugFlags() { c.Println("DebugFlags called on", c.Name()) var debugflags func(*Command) @@ -1509,8 +1520,7 @@ func (c *Command) CalledAs() string { return "" } -// hasNameOrAliasPrefix returns true if the Name or any of aliases start -// with prefix +// with prefix. func (c *Command) hasNameOrAliasPrefix(prefix string) bool { if strings.HasPrefix(c.Name(), prefix) { c.commandCalledAs.name = c.Name() @@ -1525,7 +1535,7 @@ func (c *Command) hasNameOrAliasPrefix(prefix string) bool { return false } -// NameAndAliases returns a list of the command name and all aliases +// NameAndAliases returns a list of the command name and all aliases. func (c *Command) NameAndAliases() string { return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ") } @@ -1788,7 +1798,7 @@ func (c *Command) Flag(name string) (flag *zflag.Flag) { flag = c.persistentFlag(name) } - return + return flag } // Recursively find matching persistent zflag. @@ -1801,7 +1811,7 @@ func (c *Command) persistentFlag(name string) (flag *zflag.Flag) { c.updateParentsPflags() flag = c.parentsPflags.Lookup(name) } - return + return flag } // ParseFlags parses persistent flag tree and local flags. diff --git a/command_notwin.go b/command_notwin.go index 6cf49ff..aa387d1 100644 --- a/command_notwin.go +++ b/command_notwin.go @@ -3,4 +3,4 @@ package zulu -var preExecHookFn func(*Command) +func runMouseTrap(command *Command) {} diff --git a/command_test.go b/command_test.go index 67fb1db..38db014 100644 --- a/command_test.go +++ b/command_test.go @@ -13,6 +13,7 @@ import ( "github.com/zulucmd/zflag/v2" "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func noopRun(*zulu.Command, []string) error { return nil } @@ -22,14 +23,13 @@ func executeCommand(root *zulu.Command, args ...string) (output string, err erro return output, err } -func executeCommandWithContext(ctx context.Context, root *zulu.Command, args ...string) (output string, err error) { +func executeCommandWithContext(ctx context.Context, root *zulu.Command, args ...string) (string, error) { buf := new(bytes.Buffer) root.SetOut(buf) root.SetErr(buf) root.SetArgs(args) - err = root.ExecuteContext(ctx) - + err := root.ExecuteContext(ctx) return buf.String(), err } @@ -44,7 +44,11 @@ func executeCommandC(root *zulu.Command, args ...string) (c *zulu.Command, outpu return c, buf.String(), err } -func executeCommandWithContextC(ctx context.Context, root *zulu.Command, args ...string) (c *zulu.Command, output string, err error) { +func executeCommandWithContextC( + ctx context.Context, + root *zulu.Command, + args ...string, +) (c *zulu.Command, output string, err error) { buf := new(bytes.Buffer) root.SetOut(buf) root.SetErr(buf) @@ -72,9 +76,9 @@ func TestSingleCommand(t *testing.T) { rootCmd.AddCommand(aCmd, bCmd) output, err := executeCommand(rootCmd, "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(rootCmdArgs, " "), "rootCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(rootCmdArgs, " "), "rootCmdArgs unexpected") } func TestChildCommand(t *testing.T) { @@ -89,15 +93,15 @@ func TestChildCommand(t *testing.T) { rootCmd.AddCommand(child1Cmd, child2Cmd) output, err := executeCommand(rootCmd, "child1", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(child1CmdArgs, " "), "child1CmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(child1CmdArgs, " "), "child1CmdArgs unexpected") } func TestCallCommandWithoutSubcommands(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Args: zulu.NoArgs, RunE: noopRun} _, err := executeCommand(rootCmd) - assertNilf(t, err, "Calling command without subcommands should not have error") + testutil.AssertNilf(t, err, "Calling command without subcommands should not have error") } func TestRootExecuteUnknownCommand(t *testing.T) { @@ -107,7 +111,7 @@ func TestRootExecuteUnknownCommand(t *testing.T) { output, _ := executeCommand(rootCmd, "unknown") expected := "Error: unknown command \"unknown\" for \"root\"\nRun 'root --help' for usage.\n" - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestSubcommandExecuteC(t *testing.T) { @@ -116,16 +120,16 @@ func TestSubcommandExecuteC(t *testing.T) { rootCmd.AddCommand(childCmd) c, output, err := executeCommandC(rootCmd, "child") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, "child", c.Name(), "`invalid command returned from ExecuteC") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, "child", c.Name(), "`invalid command returned from ExecuteC") } func TestExecuteContext(t *testing.T) { ctx := context.TODO() ctxRun := func(cmd *zulu.Command, args []string) error { - assertEqualf(t, ctx, cmd.Context(), "Command %q must have context when called with ExecuteContext", cmd.Use) + testutil.AssertEqualf(t, ctx, cmd.Context(), "Command %q must have context when called with ExecuteContext", cmd.Use) return nil } @@ -137,20 +141,20 @@ func TestExecuteContext(t *testing.T) { rootCmd.AddCommand(childCmd) _, err := executeCommandWithContext(ctx, rootCmd, "") - assertNilf(t, err, "Root command must not fail") + testutil.AssertNilf(t, err, "Root command must not fail") _, err = executeCommandWithContext(ctx, rootCmd, "child") - assertNilf(t, err, "Subcommand must not fail") + testutil.AssertNilf(t, err, "Subcommand must not fail") _, err = executeCommandWithContext(ctx, rootCmd, "child", "grandchild") - assertNilf(t, err, "Command child must not fail") + testutil.AssertNilf(t, err, "Command child must not fail") } func TestExecuteContextC(t *testing.T) { ctx := context.TODO() ctxRun := func(cmd *zulu.Command, args []string) error { - assertEqualf(t, ctx, cmd.Context(), "Command %q must have context when called with ExecuteContext", cmd.Use) + testutil.AssertEqualf(t, ctx, cmd.Context(), "Command %q must have context when called with ExecuteContext", cmd.Use) return nil } @@ -162,18 +166,18 @@ func TestExecuteContextC(t *testing.T) { rootCmd.AddCommand(childCmd) _, _, err := executeCommandWithContextC(ctx, rootCmd, "") - assertNilf(t, err, "Root command must not fail") + testutil.AssertNilf(t, err, "Root command must not fail") _, _, err = executeCommandWithContextC(ctx, rootCmd, "child") - assertNilf(t, err, "Subcommand must not fail") + testutil.AssertNilf(t, err, "Subcommand must not fail") _, _, err = executeCommandWithContextC(ctx, rootCmd, "child", "grandchild") - assertNilf(t, err, "Command child must not fail") + testutil.AssertNilf(t, err, "Command child must not fail") } func TestExecute_NoContext(t *testing.T) { run := func(cmd *zulu.Command, args []string) error { - assertEqualf(t, context.Background(), cmd.Context(), "Command %s must have background context", cmd.Use) + testutil.AssertEqualf(t, context.Background(), cmd.Context(), "Command %s must have background context", cmd.Use) return nil } @@ -185,13 +189,13 @@ func TestExecute_NoContext(t *testing.T) { rootCmd.AddCommand(childCmd) _, err := executeCommand(rootCmd, "") - assertNilf(t, err, "Root command must not fail") + testutil.AssertNilf(t, err, "Root command must not fail") _, err = executeCommand(rootCmd, "child") - assertNilf(t, err, "Subcommand must not fail") + testutil.AssertNilf(t, err, "Subcommand must not fail") _, err = executeCommand(rootCmd, "child", "grandchild") - assertNilf(t, err, "Command child must not fail") + testutil.AssertNilf(t, err, "Command child must not fail") } func TestRootUnknownCommandSilenced(t *testing.T) { @@ -201,7 +205,7 @@ func TestRootUnknownCommandSilenced(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "child", RunE: noopRun}) output, _ := executeCommand(rootCmd, "unknown") - assertEqualf(t, "", output, "Expected blank output, because of silenced usage") + testutil.AssertEqualf(t, "", output, "Expected blank output, because of silenced usage") } func TestCommandAlias(t *testing.T) { @@ -222,9 +226,9 @@ func TestCommandAlias(t *testing.T) { rootCmd.AddCommand(echoCmd) output, err := executeCommand(rootCmd, "tell", "times", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(timesCmdArgs, " "), "timesCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(timesCmdArgs, " "), "timesCmdArgs unexpected") } func TestEnablePrefixMatching(t *testing.T) { @@ -241,9 +245,9 @@ func TestEnablePrefixMatching(t *testing.T) { rootCmd.AddCommand(aCmd, bCmd) output, err := executeCommand(rootCmd, "a", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(aCmdArgs, " "), "aCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(aCmdArgs, " "), "aCmdArgs unexpected") zulu.EnablePrefixMatching = false } @@ -268,9 +272,9 @@ func TestAliasPrefixMatching(t *testing.T) { rootCmd.AddCommand(echoCmd) output, err := executeCommand(rootCmd, "sa", "times", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(timesCmdArgs, " "), "timesCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(timesCmdArgs, " "), "timesCmdArgs unexpected") zulu.EnablePrefixMatching = false } @@ -290,9 +294,9 @@ func TestChildSameName(t *testing.T) { rootCmd.AddCommand(fooCmd, barCmd) output, err := executeCommand(rootCmd, "foo", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(fooCmdArgs, " "), "fooCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(fooCmdArgs, " "), "fooCmdArgs unexpected") } // TestGrandChildSameName checks the correct behaviour of zulu in cases, @@ -311,9 +315,9 @@ func TestGrandChildSameName(t *testing.T) { rootCmd.AddCommand(barCmd) output, err := executeCommand(rootCmd, "bar", "foo", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(fooCmdArgs, " "), "fooCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(fooCmdArgs, " "), "fooCmdArgs unexpected") } func TestFlagLong(t *testing.T) { @@ -330,12 +334,12 @@ func TestFlagLong(t *testing.T) { c.Flags().StringVar(&stringFlagValue, "sf", "", "") output, err := executeCommand(c, "--intf=7", "--sf=abc", "one", "--", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 1, c.ArgsLenAtDash(), "Unexpected ArgsLenAtDash") - assertEqualf(t, 7, intFlagValue, "Unexpected intFlagValue") - assertEqualf(t, "abc", stringFlagValue, "Unexpected stringFlagValue") - assertEqualf(t, onetwo, strings.Join(cArgs, " "), "rootCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 1, c.ArgsLenAtDash(), "Unexpected ArgsLenAtDash") + testutil.AssertEqualf(t, 7, intFlagValue, "Unexpected intFlagValue") + testutil.AssertEqualf(t, "abc", stringFlagValue, "Unexpected stringFlagValue") + testutil.AssertEqualf(t, onetwo, strings.Join(cArgs, " "), "rootCmdArgs unexpected") } func TestFlagShort(t *testing.T) { @@ -352,11 +356,11 @@ func TestFlagShort(t *testing.T) { c.Flags().StringVar(&stringFlagValue, "sf", "", "", zflag.OptShorthand('s')) output, err := executeCommand(c, "-i", "7", "-sabc", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 7, intFlagValue, "Unexpected intFlagValue") - assertEqualf(t, "abc", stringFlagValue, "Unexpected stringFlagValue") - assertEqualf(t, onetwo, strings.Join(cArgs, " "), "rootCmdArgs unexpected") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 7, intFlagValue, "Unexpected intFlagValue") + testutil.AssertEqualf(t, "abc", stringFlagValue, "Unexpected stringFlagValue") + testutil.AssertEqualf(t, onetwo, strings.Join(cArgs, " "), "rootCmdArgs unexpected") } func TestChildFlag(t *testing.T) { @@ -368,9 +372,9 @@ func TestChildFlag(t *testing.T) { childCmd.Flags().IntVar(&intFlagValue, "intf", -1, "", zflag.OptShorthand('i')) output, err := executeCommand(rootCmd, "child", "-i7") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 7, intFlagValue, "Unexpected flag value:") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 7, intFlagValue, "Unexpected flag value:") } func TestChildFlagWithParentLocalFlag(t *testing.T) { @@ -383,9 +387,9 @@ func TestChildFlagWithParentLocalFlag(t *testing.T) { childCmd.Flags().IntVar(&intFlagValue, "intf", -1, "", zflag.OptShorthand('i')) _, err := executeCommand(rootCmd, "child", "-i7", "-sabc") - assertNotNilf(t, err, "Invalid flag value should generate error") - assertContains(t, err.Error(), "unknown shorthand") - assertEqualf(t, 7, intFlagValue, "Unexpected flag value:") + testutil.AssertNotNilf(t, err, "Invalid flag value should generate error") + testutil.AssertContains(t, err.Error(), "unknown shorthand") + testutil.AssertEqualf(t, 7, intFlagValue, "Unexpected flag value:") } func TestFlagInvalidInput(t *testing.T) { @@ -393,8 +397,8 @@ func TestFlagInvalidInput(t *testing.T) { rootCmd.Flags().Int("intf", -1, "", zflag.OptShorthand('i')) _, err := executeCommand(rootCmd, "-iabc") - assertNotNilf(t, err, "Invalid flag value should generate error") - assertContains(t, err.Error(), "invalid syntax") + testutil.AssertNotNilf(t, err, "Invalid flag value should generate error") + testutil.AssertContains(t, err.Error(), "invalid syntax") } func TestFlagBeforeCommand(t *testing.T) { @@ -407,13 +411,13 @@ func TestFlagBeforeCommand(t *testing.T) { // With short flag. _, err := executeCommand(rootCmd, "-i7", "child") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 7, flagValue, "Unexpected flag value:") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 7, flagValue, "Unexpected flag value:") // With long flag. _, err = executeCommand(rootCmd, "--intf=8", "child") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 8, flagValue, "Unexpected flag value:") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 8, flagValue, "Unexpected flag value:") } func TestStripFlags(t *testing.T) { @@ -499,8 +503,8 @@ func TestDisableFlagParsing(t *testing.T) { args := []string{"cmd", "-v", "-race", "-file", "foo.go"} output, err := executeCommand(c, args...) - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") if !reflect.DeepEqual(args, cArgs) { t.Errorf("Expected: %v, got: %v", args, cArgs) @@ -519,10 +523,10 @@ func TestPersistentFlagsOnSameCommand(t *testing.T) { rootCmd.PersistentFlags().IntVar(&flagValue, "intf", -1, "", zflag.OptShorthand('i')) output, err := executeCommand(rootCmd, "-i7", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(rootCmdArgs, " "), "rootCmdArgs unexpected") - assertEqualf(t, 7, flagValue, "Unexpected flag value:") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(rootCmdArgs, " "), "rootCmdArgs unexpected") + testutil.AssertEqualf(t, 7, flagValue, "Unexpected flag value:") } // TestEmptyInputs checks, @@ -534,9 +538,9 @@ func TestEmptyInputs(t *testing.T) { c.Flags().IntVar(&flagValue, "intf", -1, "", zflag.OptShorthand('i')) output, err := executeCommand(c, "", "-i7", "") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 7, flagValue, "Unexpected flag value:") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 7, flagValue, "Unexpected flag value:") } func TestChildFlagShadowsParentPersistentFlag(t *testing.T) { @@ -553,10 +557,10 @@ func TestChildFlagShadowsParentPersistentFlag(t *testing.T) { childInherited := child.InheritedFlags() childLocal := child.LocalFlags() - assertNotNilf(t, childLocal.Lookup("strf"), `LocalFlags expected to contain "strf"`) - assertNotNilf(t, childInherited.Lookup("boolf"), `InheritedFlags expected to contain "boolf"`) - assertNilf(t, childInherited.Lookup("intf"), `InheritedFlags should not contain shadowed flag "intf"`) - assertNotNilf(t, childLocal.Lookup("intf"), `LocalFlags expected to contain "intf"`) + testutil.AssertNotNilf(t, childLocal.Lookup("strf"), `LocalFlags expected to contain "strf"`) + testutil.AssertNotNilf(t, childInherited.Lookup("boolf"), `InheritedFlags expected to contain "boolf"`) + testutil.AssertNilf(t, childInherited.Lookup("intf"), `InheritedFlags should not contain shadowed flag "intf"`) + testutil.AssertNotNilf(t, childLocal.Lookup("intf"), `LocalFlags expected to contain "intf"`) } func TestPersistentFlagsOnChild(t *testing.T) { @@ -575,11 +579,11 @@ func TestPersistentFlagsOnChild(t *testing.T) { childCmd.Flags().IntVar(&childFlagValue, "childf", -1, "", zflag.OptShorthand('c')) output, err := executeCommand(rootCmd, "child", "-c7", "-p8", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, onetwo, strings.Join(childCmdArgs, " "), "rootCmdArgs unexpected") - assertEqualf(t, 8, parentFlagValue, "Unexpected parentFlagValue:") - assertEqualf(t, 7, childFlagValue, "Unexpected childFlagValue:") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, onetwo, strings.Join(childCmdArgs, " "), "rootCmdArgs unexpected") + testutil.AssertEqualf(t, 8, parentFlagValue, "Unexpected parentFlagValue:") + testutil.AssertEqualf(t, 7, childFlagValue, "Unexpected childFlagValue:") } func TestRequiredFlags(t *testing.T) { @@ -590,7 +594,7 @@ func TestRequiredFlags(t *testing.T) { expected := fmt.Sprintf("required flag(s) %q, %q not set", "--foo1", "--foo2") _, err := executeCommand(c) - assertEqualf(t, expected, err.Error(), "Unexpected error") + testutil.AssertEqualf(t, expected, err.Error(), "Unexpected error") } func TestRequiredFlagsWithCustomFlagErrorFunc(t *testing.T) { @@ -605,9 +609,9 @@ func TestRequiredFlagsWithCustomFlagErrorFunc(t *testing.T) { requiredFlagErrorMessage := fmt.Sprintf("required flag(s) %q not set", "--foo1") output, err := executeCommand(c) - assertEqualf(t, silentError, err.Error(), "Unexpected error:") - assertContains(t, output, requiredFlagErrorMessage) - assertContains(t, output, c.UsageString()) + testutil.AssertEqualf(t, silentError, err.Error(), "Unexpected error:") + testutil.AssertContains(t, output, requiredFlagErrorMessage) + testutil.AssertContains(t, output, c.UsageString()) } func TestPersistentRequiredFlags(t *testing.T) { @@ -626,7 +630,7 @@ func TestPersistentRequiredFlags(t *testing.T) { expected := fmt.Sprintf("required flag(s) %q, %q, %q, %q not set", "--bar1", "--bar2", "--foo1", "--foo2") _, err := executeCommand(parent, "child") - assertEqualf(t, expected, err.Error(), "Unexpected error:") + testutil.AssertEqualf(t, expected, err.Error(), "Unexpected error:") } func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) { @@ -643,17 +647,17 @@ func TestPersistentRequiredFlagsWithDisableFlagParsing(t *testing.T) { parent.AddCommand(child) _, err := executeCommand(parent, "--foo", "child") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") // Reset the flag or else it will remember the state from the previous command flag.Changed = false _, err = executeCommand(parent, "child", "--foo") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") // Reset the flag or else it will remember the state from the previous command flag.Changed = false _, err = executeCommand(parent, "child") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestInitHelpFlagMergesFlags(t *testing.T) { @@ -665,7 +669,7 @@ func TestInitHelpFlagMergesFlags(t *testing.T) { childCmd.InitDefaultHelpFlag() got := childCmd.Flags().Lookup("help").Usage - assertEqualf(t, usage, got, "Unexpected help flag usage of root command:") + testutil.AssertEqualf(t, usage, got, "Unexpected help flag usage of root command:") } func TestHelpCommandExecuted(t *testing.T) { @@ -673,8 +677,8 @@ func TestHelpCommandExecuted(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "child", RunE: noopRun}) output, err := executeCommand(rootCmd, "help") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, rootCmd.Long) + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, rootCmd.Long) } func TestHelpCommandExecutedOnChild(t *testing.T) { @@ -683,8 +687,8 @@ func TestHelpCommandExecutedOnChild(t *testing.T) { rootCmd.AddCommand(childCmd) output, err := executeCommand(rootCmd, "help", "child") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, childCmd.Long) + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, childCmd.Long) } func TestHelpCommandExecutedOnChildWithFlagThatShadowsParentFlag(t *testing.T) { @@ -698,7 +702,7 @@ func TestHelpCommandExecutedOnChildWithFlagThatShadowsParentFlag(t *testing.T) { child.Flags().Bool("baz", false, "child baz usage") got, err := executeCommand(parent, "help", "child") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") expected := `Usage: parent child [flags] @@ -712,7 +716,7 @@ Global Flags: --bar parent bar usage ` - assertEqualf(t, expected, rmCarriageRet(got), "Unexpected help text") + testutil.AssertEqualf(t, expected, rmCarriageRet(got), "Unexpected help text") } func TestSetHelpCommand(t *testing.T) { @@ -729,16 +733,16 @@ func TestSetHelpCommand(t *testing.T) { }) got, err := executeCommand(c, "help") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, expected, rmCarriageRet(got), "Unexpected help text") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, expected, rmCarriageRet(got), "Unexpected help text") } func TestHelpFlagExecuted(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Long: "Long description", RunE: noopRun} output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, rootCmd.Long) + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, rootCmd.Long) } func TestHelpFlagExecutedOnChild(t *testing.T) { @@ -747,8 +751,8 @@ func TestHelpFlagExecutedOnChild(t *testing.T) { rootCmd.AddCommand(childCmd) output, err := executeCommand(rootCmd, "child", "--help") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, childCmd.Long) + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, childCmd.Long) } // TestHelpFlagInHelp checks, @@ -762,15 +766,15 @@ func TestHelpFlagInHelp(t *testing.T) { parentCmd.AddCommand(childCmd) output, err := executeCommand(parentCmd, "help", "child") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "[flags]") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "[flags]") } func TestFlagsInUsage(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Args: zulu.NoArgs, RunE: func(*zulu.Command, []string) error { return nil }} output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "[flags]") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "[flags]") } func TestHelpExecutedOnNonRunnableChild(t *testing.T) { @@ -779,32 +783,32 @@ func TestHelpExecutedOnNonRunnableChild(t *testing.T) { rootCmd.AddCommand(childCmd) output, err := executeCommand(rootCmd, "child") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, childCmd.Long) + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, childCmd.Long) } func TestVersionFlagExecuted(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Version: "1.0.0", RunE: noopRun} output, err := executeCommand(rootCmd, "--version", "arg1") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "root version 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "root version 1.0.0") } func TestVersionFlagExecutedWithNoName(t *testing.T) { rootCmd := &zulu.Command{Version: "1.0.0", RunE: noopRun} output, err := executeCommand(rootCmd, "--version", "arg1") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "version 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "version 1.0.0") } func TestShortAndLongVersionFlagInHelp(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Version: "1.0.0", RunE: noopRun} output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "-v, --version") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "-v, --version") } func TestLongVersionFlagOnlyInHelpWhenShortPredefined(t *testing.T) { @@ -812,17 +816,17 @@ func TestLongVersionFlagOnlyInHelpWhenShortPredefined(t *testing.T) { rootCmd.Flags().String("foo", "", "not a version flag", zflag.OptShorthand('v')) output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") - assertNotContains(t, output, "-v, --version") - assertContains(t, output, "--version") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertNotContains(t, output, "-v, --version") + testutil.AssertContains(t, output, "--version") } func TestShorthandVersionFlagExecuted(t *testing.T) { rootCmd := &zulu.Command{Use: "root", Version: "1.0.0", RunE: noopRun} output, err := executeCommand(rootCmd, "-v", "arg1") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "root version 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "root version 1.0.0") } func TestVersionTemplate(t *testing.T) { @@ -830,8 +834,8 @@ func TestVersionTemplate(t *testing.T) { rootCmd.SetVersionTemplate(`customized version: {{.Version}}`) output, err := executeCommand(rootCmd, "--version", "arg1") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "customized version: 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "customized version: 1.0.0") } func TestShorthandVersionTemplate(t *testing.T) { @@ -839,8 +843,8 @@ func TestShorthandVersionTemplate(t *testing.T) { rootCmd.SetVersionTemplate(`customized version: {{.Version}}`) output, err := executeCommand(rootCmd, "-v", "arg1") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "customized version: 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "customized version: 1.0.0") } func TestVersionFlagExecutedOnSubcommand(t *testing.T) { @@ -848,8 +852,8 @@ func TestVersionFlagExecutedOnSubcommand(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "sub", RunE: noopRun}) output, err := executeCommand(rootCmd, "--version", "sub") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "root version 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "root version 1.0.0") } func TestShorthandVersionFlagExecutedOnSubcommand(t *testing.T) { @@ -857,8 +861,8 @@ func TestShorthandVersionFlagExecutedOnSubcommand(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "sub", RunE: noopRun}) output, err := executeCommand(rootCmd, "-v", "sub") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "root version 1.0.0") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "root version 1.0.0") } func TestVersionFlagOnlyAddedToRoot(t *testing.T) { @@ -866,8 +870,8 @@ func TestVersionFlagOnlyAddedToRoot(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "sub", RunE: noopRun}) _, err := executeCommand(rootCmd, "sub", "--version") - assertNotNilf(t, err, "Expected an error") - assertContains(t, err.Error(), "unknown flag: --version") + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, err.Error(), "unknown flag: --version") } func TestShortVersionFlagOnlyAddedToRoot(t *testing.T) { @@ -875,24 +879,24 @@ func TestShortVersionFlagOnlyAddedToRoot(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "sub", RunE: noopRun}) _, err := executeCommand(rootCmd, "sub", "-v") - assertNotNilf(t, err, "Expected an error") - assertContains(t, err.Error(), "unknown shorthand flag: 'v' in -v") + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, err.Error(), "unknown shorthand flag: 'v' in -v") } func TestVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) { rootCmd := &zulu.Command{Use: "root", RunE: noopRun} _, err := executeCommand(rootCmd, "--version") - assertNotNilf(t, err, "Expected an error") - assertContains(t, err.Error(), "unknown flag: --version") + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, err.Error(), "unknown flag: --version") } func TestShorthandVersionFlagOnlyExistsIfVersionNonEmpty(t *testing.T) { rootCmd := &zulu.Command{Use: "root", RunE: noopRun} _, err := executeCommand(rootCmd, "-v") - assertNotNilf(t, err, "Expected an error") - assertContains(t, err.Error(), "unknown shorthand flag: 'v' in -v") + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, err.Error(), "unknown shorthand flag: 'v' in -v") } func TestShorthandVersionFlagOnlyAddedIfShorthandNotDefined(t *testing.T) { @@ -900,9 +904,9 @@ func TestShorthandVersionFlagOnlyAddedIfShorthandNotDefined(t *testing.T) { rootCmd.Flags().String("notversion", "", "not a version flag", zflag.OptShorthand('v')) _, err := executeCommand(rootCmd, "-v") - assertNotNilf(t, err, "Expected an error") - assertContains(t, rootCmd.Flags().ShorthandLookupStr("v").Name, "notversion") - assertContains(t, err.Error(), "flag needs an argument: 'v' in -v") + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, rootCmd.Flags().ShorthandLookupStr("v").Name, "notversion") + testutil.AssertContains(t, err.Error(), "flag needs an argument: 'v' in -v") } func TestShorthandVersionFlagOnlyAddedIfVersionNotDefined(t *testing.T) { @@ -910,8 +914,8 @@ func TestShorthandVersionFlagOnlyAddedIfVersionNotDefined(t *testing.T) { rootCmd.Flags().Bool("version", false, "a different kind of version flag") _, err := executeCommand(rootCmd, "-v") - assertNotNilf(t, err, "Expected an error") - assertContains(t, err.Error(), "unknown shorthand flag: 'v' in -v") + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertContains(t, err.Error(), "unknown shorthand flag: 'v' in -v") } func TestUsageIsNotPrintedTwice(t *testing.T) { @@ -920,7 +924,7 @@ func TestUsageIsNotPrintedTwice(t *testing.T) { cmd.AddCommand(sub) output, _ := executeCommand(cmd, "") - assertEqualf(t, strings.Count(output, "Usage:"), 1, "Usage output is not printed exactly once") + testutil.AssertEqualf(t, strings.Count(output, "Usage:"), 1, "Usage output is not printed exactly once") } func TestVisitParents(t *testing.T) { @@ -935,15 +939,15 @@ func TestVisitParents(t *testing.T) { total++ } sub.VisitParents(add) - assertEqualf(t, 1, total, "Unexpected parent visits") + testutil.AssertEqualf(t, 1, total, "Unexpected parent visits") total = 0 dsub.VisitParents(add) - assertEqualf(t, 2, total, "Unexpected parent visits") + testutil.AssertEqualf(t, 2, total, "Unexpected parent visits") total = 0 c.VisitParents(add) - assertEqualf(t, 0, total, "Unexpected parent visits") + testutil.AssertEqualf(t, 0, total, "Unexpected parent visits") } func TestSuggestions(t *testing.T) { @@ -955,8 +959,16 @@ func TestSuggestions(t *testing.T) { } rootCmd.AddCommand(timesCmd) - templateWithSuggestions := "Error: unknown command \"%s\" for \"root\"\n\nDid you mean this?\n\t%s\n\nRun 'root --help' for usage.\n" - templateWithoutSuggestions := "Error: unknown command \"%s\" for \"root\"\nRun 'root --help' for usage.\n" + templateWithSuggestions := `Error: unknown command "%s" for "root" + +Did you mean this? + %s + +Run 'root --help' for usage. +` + templateWithoutSuggestions := `Error: unknown command "%s" for "root" +Run 'root --help' for usage. +` tests := map[string]string{ "time": "times", @@ -980,11 +992,11 @@ func TestSuggestions(t *testing.T) { output, _ := executeCommand(rootCmd, typo) if suggestion == "" || suggestionsDisabled { - assertEqualf(t, fmt.Sprintf(templateWithoutSuggestions, typo), output, "Unexpected response") + testutil.AssertEqualf(t, fmt.Sprintf(templateWithoutSuggestions, typo), output, "Unexpected response") continue } - assertEqualf(t, fmt.Sprintf(templateWithSuggestions, typo, suggestion), output, "Unexpected response") + testutil.AssertEqualf(t, fmt.Sprintf(templateWithSuggestions, typo, suggestion), output, "Unexpected response") } } } @@ -996,7 +1008,7 @@ func TestRemoveCommand(t *testing.T) { rootCmd.RemoveCommand(childCmd) _, err := executeCommand(rootCmd, "child") - assertNotNilf(t, err, "Expected error on calling removed command.") + testutil.AssertNotNilf(t, err, "Expected error on calling removed command.") } func TestReplaceCommandWithRemove(t *testing.T) { @@ -1015,10 +1027,10 @@ func TestReplaceCommandWithRemove(t *testing.T) { rootCmd.AddCommand(child2Cmd) output, err := executeCommand(rootCmd, "child") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertNotEqualf(t, 1, childUsed, "Removed command shouldn't be called") - assertEqualf(t, 2, childUsed, "Replacing command should have been called but didn't") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertNotEqualf(t, 1, childUsed, "Removed command shouldn't be called") + testutil.AssertEqualf(t, 2, childUsed, "Replacing command should have been called but didn't") } func TestDeprecatedCommand(t *testing.T) { @@ -1031,8 +1043,8 @@ func TestDeprecatedCommand(t *testing.T) { rootCmd.AddCommand(deprecatedCmd) output, err := executeCommand(rootCmd, "deprecated") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, deprecatedCmd.Deprecated) + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, deprecatedCmd.Deprecated) } func TestHooks(t *testing.T) { @@ -1069,8 +1081,8 @@ func TestHooks(t *testing.T) { } output, err := executeCommand(c, "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") for _, v := range []struct { name string @@ -1082,7 +1094,7 @@ func TestHooks(t *testing.T) { {"postArgs", postArgs}, {"persPostArgs", persPostArgs}, } { - assertEqualf(t, onetwo, v.got, "%s unexpected", v.name) + testutil.AssertEqualf(t, onetwo, v.got, "%s unexpected", v.name) } } @@ -1099,8 +1111,8 @@ func TestHooksVersionFlagAddedWhenVersionSetOnInitialize(t *testing.T) { } output, err := executeCommand(c, "--version") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, "c version (devel)\n", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, "c version (devel)\n", output, "Unexpected output") } func TestPersistentHooks(t *testing.T) { @@ -1159,8 +1171,8 @@ func TestPersistentHooks(t *testing.T) { childCmd.OnPersistentFinalize(getTestHookFn("persChildPersFinArgs")) output, err := executeCommand(parentCmd, "child", "one", "two") - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") for _, v := range []struct { name string @@ -1210,11 +1222,11 @@ func TestPersistentHooks(t *testing.T) { } { got, ok := hooksArgs[v.name] if !ok { - assertEqualf(t, v.expected, "", "Expected %q to be called, but it wasn't", v.name) + testutil.AssertEqualf(t, v.expected, "", "Expected %q to be called, but it wasn't", v.name) continue } - assertEqualf(t, v.expected, got, "Expected %q %s, got %q", v.expected, v.name, got) + testutil.AssertEqualf(t, v.expected, got, "Expected %q %s, got %q", v.expected, v.name, got) } } @@ -1229,8 +1241,18 @@ func TestGlobalNormFuncPropagation(t *testing.T) { rootCmd.AddCommand(childCmd) rootCmd.SetGlobalNormalizationFunc(normFunc) - assertEqualf(t, reflect.ValueOf(normFunc).Pointer(), reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer(), "rootCmd seems to have a wrong normalization function") - assertEqualf(t, reflect.ValueOf(normFunc).Pointer(), reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer(), "childCmd should have had the normalization function of rootCmd") + testutil.AssertEqualf( + t, + reflect.ValueOf(normFunc).Pointer(), + reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer(), + "rootCmd seems to have a wrong normalization function", + ) + testutil.AssertEqualf( + t, + reflect.ValueOf(normFunc).Pointer(), + reflect.ValueOf(childCmd.GlobalNormalizationFunc()).Pointer(), + "childCmd should have had the normalization function of rootCmd", + ) } // Related to https://github.com/spf13/cobra/issues/521. @@ -1242,7 +1264,12 @@ func TestNormPassedOnLocal(t *testing.T) { c := &zulu.Command{} c.Flags().Bool("flagname", true, "this is a dummy flag") c.SetGlobalNormalizationFunc(toUpper) - assertEqualf(t, c.LocalFlags().Lookup("flagname"), c.LocalFlags().Lookup("FLAGNAME"), "Normalization function should be passed on to Local flag set") + testutil.AssertEqualf( + t, + c.LocalFlags().Lookup("flagname"), + c.LocalFlags().Lookup("FLAGNAME"), + "Normalization function should be passed on to Local flag set", + ) } // Related to https://github.com/spf13/cobra/issues/521. @@ -1263,12 +1290,30 @@ func TestNormPassedOnInherited(t *testing.T) { c.AddCommand(child2) inherited := child1.InheritedFlags() - assertNotNilf(t, inherited.Lookup("flagname"), "Normalization function passed on inherited flag should not be nil") - assertEqualf(t, inherited.Lookup("flagname"), inherited.Lookup("FLAGNAME"), "Normalization function should be passed on to inherited flag set in command added before flag") + testutil.AssertNotNilf( + t, + inherited.Lookup("flagname"), + "Normalization function passed on inherited flag should not be nil", + ) + testutil.AssertEqualf( + t, + inherited.Lookup("flagname"), + inherited.Lookup("FLAGNAME"), + "Normalization function should be passed on to inherited flag set in command added before flag", + ) inherited = child2.InheritedFlags() - assertNotNilf(t, inherited.Lookup("flagname"), "Normalization function passed on inherited flag should not be nil") - assertEqualf(t, inherited.Lookup("flagname"), inherited.Lookup("FLAGNAME"), "Normalization function should be passed on to inherited flag set in command added after flag") + testutil.AssertNotNilf( + t, + inherited.Lookup("flagname"), + "Normalization function passed on inherited flag should not be nil", + ) + testutil.AssertEqualf( + t, + inherited.Lookup("flagname"), + inherited.Lookup("FLAGNAME"), + "Normalization function should be passed on to inherited flag set in command added after flag", + ) } // Related to https://github.com/spf13/cobra/issues/521. @@ -1285,7 +1330,12 @@ func TestConsistentNormalizedName(t *testing.T) { c.SetGlobalNormalizationFunc(toUpper) c.SetGlobalNormalizationFunc(n) - assertNotEqualf(t, c.LocalFlags().Lookup("flagname"), c.LocalFlags().Lookup("FLAGNAME"), "Normalizing flag names should not result in duplicate flags") + testutil.AssertNotEqualf( + t, + c.LocalFlags().Lookup("flagname"), + c.LocalFlags().Lookup("FLAGNAME"), + "Normalizing flag names should not result in duplicate flags", + ) } func TestFlagOnZflagCommandLine(t *testing.T) { @@ -1296,7 +1346,7 @@ func TestFlagOnZflagCommandLine(t *testing.T) { c.AddCommand(&zulu.Command{Use: "child", RunE: noopRun}) output, _ := executeCommand(c, "--help") - assertContains(t, output, flagName) + testutil.AssertContains(t, output, flagName) resetCommandLineFlagSet() } @@ -1312,15 +1362,15 @@ func TestHiddenCommandExecutes(t *testing.T) { } output, err := executeCommand(c) - assertEqualf(t, "", output, "Unexpected output") - assertNilf(t, err, "Unexpected error") - assertEqualf(t, true, executed, "Hidden command should have been executed") + testutil.AssertEqualf(t, "", output, "Unexpected output") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, true, executed, "Hidden command should have been executed") } -// test to ensure hidden commands do not show up in usage/help text +// test to ensure hidden commands do not show up in usage/help text. func TestHiddenCommandIsHidden(t *testing.T) { c := &zulu.Command{Use: "c", Hidden: true, RunE: noopRun} - assertEqualf(t, false, c.IsAvailableCommand(), "Hidden command should be unavailable") + testutil.AssertEqualf(t, false, c.IsAvailableCommand(), "Hidden command should be unavailable") } func TestCommandsAreSorted(t *testing.T) { @@ -1336,7 +1386,7 @@ func TestCommandsAreSorted(t *testing.T) { } for i, c := range rootCmd.Commands() { - assertEqual(t, expectedNames[i], c.Name()) + testutil.AssertEqual(t, expectedNames[i], c.Name()) } zulu.EnableCommandSorting = true @@ -1354,41 +1404,51 @@ func TestEnableCommandSortingIsDisabled(t *testing.T) { } for i, c := range rootCmd.Commands() { - assertEqual(t, originalNames[i], c.Name()) + testutil.AssertEqual(t, originalNames[i], c.Name()) } zulu.EnableCommandSorting = true } func TestUsageWithGroup(t *testing.T) { - var rootCmd = &zulu.Command{Use: "root", Short: "test", CompletionOptions: zulu.CompletionOptions{DisableDefaultCmd: true}, RunE: noopRun} + var rootCmd = &zulu.Command{ + Use: "root", + Short: "test", + CompletionOptions: zulu.CompletionOptions{DisableDefaultCmd: true}, + RunE: noopRun, + } rootCmd.AddCommand(&zulu.Command{Use: "cmd1", Group: "group1", RunE: noopRun}) rootCmd.AddCommand(&zulu.Command{Use: "cmd2", Group: "group2", RunE: noopRun}) output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") output = rmCarriageRet(output) // help should be ungrouped here - assertContains(t, output, "\nAvailable Commands:\n help") - assertContains(t, output, "\ngroup1\n cmd1") - assertContains(t, output, "\ngroup2\n cmd2") + testutil.AssertContains(t, output, "\nAvailable Commands:\n help") + testutil.AssertContains(t, output, "\ngroup1\n cmd1") + testutil.AssertContains(t, output, "\ngroup2\n cmd2") } func TestUsageHelpGroup(t *testing.T) { - var rootCmd = &zulu.Command{Use: "root", Short: "test", CompletionOptions: zulu.CompletionOptions{DisableDefaultCmd: true}, RunE: noopRun} + var rootCmd = &zulu.Command{ + Use: "root", + Short: "test", + CompletionOptions: zulu.CompletionOptions{DisableDefaultCmd: true}, + RunE: noopRun, + } rootCmd.AddCommand(&zulu.Command{Use: "xxx", Group: "group", RunE: noopRun}) rootCmd.SetHelpCommandGroup("group") output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") output = rmCarriageRet(output) // now help should be grouped under "group" - assertNotContains(t, output, "\nAvailable Commands:\n help") - assertContains(t, output, "\nAvailable Commands:\n\ngroup\n help") + testutil.AssertNotContains(t, output, "\nAvailable Commands:\n help") + testutil.AssertContains(t, output, "\nAvailable Commands:\n\ngroup\n help") } func TestAddGroup(t *testing.T) { @@ -1398,10 +1458,10 @@ func TestAddGroup(t *testing.T) { rootCmd.AddCommand(&zulu.Command{Use: "cmd", Group: "group", RunE: noopRun}) output, err := executeCommand(rootCmd, "--help") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") output = rmCarriageRet(output) - assertContains(t, output, "\nTest group\n cmd") + testutil.AssertContains(t, output, "\nTest group\n cmd") } func TestInOutErr(t *testing.T) { @@ -1410,16 +1470,16 @@ func TestInOutErr(t *testing.T) { c.SetIn(b) c.SetOut(b) c.SetErr(b) - assertEqualf(t, b, c.InOrStdin(), "Expected setting input to be set correctly") - assertEqualf(t, b, c.OutOrStdout(), "Expected setting output to be set correctly") - assertEqualf(t, b, c.ErrOrStderr(), "Expected setting error to be set correctly") + testutil.AssertEqualf(t, b, c.InOrStdin(), "Expected setting input to be set correctly") + testutil.AssertEqualf(t, b, c.OutOrStdout(), "Expected setting output to be set correctly") + testutil.AssertEqualf(t, b, c.ErrOrStderr(), "Expected setting error to be set correctly") c.SetIn(nil) c.SetOut(nil) c.SetErr(nil) - assertEqualf(t, os.Stdin, c.InOrStdin(), "Expected setting input to nil to revert back to stdin") - assertEqualf(t, os.Stdout, c.OutOrStdout(), "Expected setting output to nil to revert back to stdout") - assertEqualf(t, os.Stderr, c.ErrOrStderr(), "Expected setting error to nil to revert back to stderr") + testutil.AssertEqualf(t, os.Stdin, c.InOrStdin(), "Expected setting input to nil to revert back to stdin") + testutil.AssertEqualf(t, os.Stdout, c.OutOrStdout(), "Expected setting output to nil to revert back to stdout") + testutil.AssertEqualf(t, os.Stderr, c.ErrOrStderr(), "Expected setting error to nil to revert back to stderr") } func TestUsageStringRedirected(t *testing.T) { @@ -1433,14 +1493,13 @@ func TestUsageStringRedirected(t *testing.T) { }) expected := "[stdout1][stderr2][stdout3]" - assertEqualf(t, expected, c.UsageString(), "Expected usage string to consider both stdout and stderr") + testutil.AssertEqualf(t, expected, c.UsageString(), "Expected usage string to consider both stdout and stderr") } func TestCommandPrintRedirection(t *testing.T) { errBuff, outBuff := bytes.NewBuffer(nil), bytes.NewBuffer(nil) root := &zulu.Command{ RunE: func(cmd *zulu.Command, args []string) error { - cmd.PrintErr("PrintErr") cmd.PrintErrln("PrintErr", "line") cmd.PrintErrf("PrintEr%s", "r") @@ -1456,16 +1515,16 @@ func TestCommandPrintRedirection(t *testing.T) { root.SetOut(outBuff) err := root.Execute() - assertNil(t, err) + testutil.AssertNil(t, err) gotErrBytes, err := io.ReadAll(errBuff) - assertNil(t, err) + testutil.AssertNil(t, err) gotOutBytes, err := io.ReadAll(outBuff) - assertNil(t, err) + testutil.AssertNil(t, err) - assertEqual(t, "PrintErrPrintErr line\nPrintErr", string(gotErrBytes)) - assertEqual(t, "PrintPrint line\nPrint", string(gotOutBytes)) + testutil.AssertEqual(t, "PrintErrPrintErr line\nPrintErr", string(gotErrBytes)) + testutil.AssertEqual(t, "PrintPrint line\nPrint", string(gotOutBytes)) } func TestFlagErrorFunc(t *testing.T) { @@ -1477,7 +1536,7 @@ func TestFlagErrorFunc(t *testing.T) { }) _, err := executeCommand(c, "--unknown-flag") - assertEqual(t, fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag"), err.Error()) + testutil.AssertEqual(t, fmt.Sprintf(expectedFmt, "unknown flag: --unknown-flag"), err.Error()) } func TestFlagErrorFuncHelp(t *testing.T) { @@ -1492,12 +1551,12 @@ func TestFlagErrorFuncHelp(t *testing.T) { expected := "Usage:\n c [flags]\n\nFlags:\n --help help for c\n" out, err := executeCommand(c, "--help") - assertNil(t, err) - assertEqual(t, expected, rmCarriageRet(out)) + testutil.AssertNil(t, err) + testutil.AssertEqual(t, expected, rmCarriageRet(out)) out, err = executeCommand(c, "-h") - assertNil(t, err) - assertEqual(t, expected, rmCarriageRet(out)) + testutil.AssertNil(t, err) + testutil.AssertEqual(t, expected, rmCarriageRet(out)) } // TestSortedFlags checks, @@ -1517,7 +1576,7 @@ func TestSortedFlags(t *testing.T) { return } if zulu.StringInSlice(f.Name, names) { - assertEqualf(t, names[i], f.Name, "Unexpected order") + testutil.AssertEqualf(t, names[i], f.Name, "Unexpected order") i++ } }) @@ -1533,7 +1592,7 @@ func TestMergeCommandLineToFlags(t *testing.T) { // help flag is not actually needed here, it's a way to enforce // zulu.Command.mergePersistentFlags is called. c.InitDefaultHelpFlag() - assertNotNilf(t, c.Flags().Lookup("boolflag"), "Expecting to have flag from CommandLine in c.Flags()") + testutil.AssertNotNilf(t, c.Flags().Lookup("boolflag"), "Expecting to have flag from CommandLine in c.Flags()") resetCommandLineFlagSet() } @@ -1543,11 +1602,17 @@ func TestMergeCommandLineToFlags(t *testing.T) { // Related to https://github.com/spf13/cobra/issues/463. func TestUseDeprecatedFlags(t *testing.T) { c := &zulu.Command{Use: "c", RunE: noopRun} - c.Flags().Bool("deprecated", false, "deprecated flag", zflag.OptShorthand('d'), zflag.OptDeprecated("This flag is deprecated")) + c.Flags().Bool( + "deprecated", + false, + "deprecated flag", + zflag.OptShorthand('d'), + zflag.OptDeprecated("This flag is deprecated"), + ) output, err := executeCommand(c, "c", "-d") - assertNilf(t, err, "Unexpected error") - assertContains(t, output, "This flag is deprecated") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertContains(t, output, "This flag is deprecated") } func TestTraverseWithParentFlags(t *testing.T) { @@ -1561,10 +1626,10 @@ func TestTraverseWithParentFlags(t *testing.T) { rootCmd.AddCommand(childCmd) c, args, err := rootCmd.Traverse([]string{"-b", "--str", "ok", "child", "--int"}) - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 1, len(args), "Unexpected args length") - // assertEqualf(t, "--add", args[0], "Wrong args") // unclear what this test was meant to do, `--add` was never added and `args[0]` == `--int` - assertEqualf(t, childCmd.Name(), c.Name(), "Expected command:") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 1, len(args), "Unexpected args length") + testutil.AssertEqualf(t, "--int", args[0], "Wrong args") + testutil.AssertEqualf(t, childCmd.Name(), c.Name(), "Expected command:") } func TestTraverseNoParentFlags(t *testing.T) { @@ -1576,9 +1641,9 @@ func TestTraverseNoParentFlags(t *testing.T) { rootCmd.AddCommand(childCmd) c, args, err := rootCmd.Traverse([]string{"child"}) - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 0, len(args), "Wrong args %v", args) - assertEqualf(t, childCmd.Name(), c.Name(), "Unexpected command") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 0, len(args), "Wrong args %v", args) + testutil.AssertEqualf(t, childCmd.Name(), c.Name(), "Unexpected command") } func TestTraverseWithBadParentFlags(t *testing.T) { @@ -1591,9 +1656,9 @@ func TestTraverseWithBadParentFlags(t *testing.T) { expected := "unknown flag: --str" c, _, err := rootCmd.Traverse([]string{"--str", "ok", "child"}) - assertNotNilf(t, err, "Expected error") - assertEqualf(t, expected, err.Error(), "Wrong error") - assertNilf(t, c, "Expected nil command") + testutil.AssertNotNilf(t, err, "Expected error") + testutil.AssertEqualf(t, expected, err.Error(), "Wrong error") + testutil.AssertNilf(t, c, "Expected nil command") } func TestTraverseWithBadChildFlag(t *testing.T) { @@ -1606,10 +1671,10 @@ func TestTraverseWithBadChildFlag(t *testing.T) { // Expect no error because the last commands args shouldn't be parsed in // Traverse. c, args, err := rootCmd.Traverse([]string{"child", "--str"}) - assertNilf(t, err, "Unexpected error") - assertEqualf(t, 1, len(args), "Unexpected args length") - assertEqualf(t, "--str", args[0], "Wrong args") - assertEqualf(t, childCmd.Name(), c.Name(), "Expected command:") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, 1, len(args), "Unexpected args length") + testutil.AssertEqualf(t, "--str", args[0], "Wrong args") + testutil.AssertEqualf(t, childCmd.Name(), c.Name(), "Expected command:") } func TestTraverseWithTwoSubcommands(t *testing.T) { @@ -1624,8 +1689,8 @@ func TestTraverseWithTwoSubcommands(t *testing.T) { subCmd.AddCommand(subsubCmd) c, _, err := rootCmd.Traverse([]string{"sub", "subsub"}) - assertNilf(t, err, "Unexpected error") - assertEqualf(t, subsubCmd.Name(), c.Name(), "Expected command:") + testutil.AssertNilf(t, err, "Unexpected error") + testutil.AssertEqualf(t, subsubCmd.Name(), c.Name(), "Expected command:") } // TestUpdateName checks if c.Name() updates on changed c.Use. @@ -1633,7 +1698,7 @@ func TestTraverseWithTwoSubcommands(t *testing.T) { func TestUpdateName(t *testing.T) { c := &zulu.Command{Use: "name xyz"} c.Use = "changedName abc" - assertEqualf(t, "changedName", c.Name(), "c.Name() should be updated on changed c.Use") + testutil.AssertEqualf(t, "changedName", c.Name(), "c.Name() should be updated on changed c.Use") } func TestCalledAs(t *testing.T) { @@ -1658,7 +1723,6 @@ func TestCalledAs(t *testing.T) { } for name, tc := range tests { - tc := tc t.Run(name, func(t *testing.T) { defer func(ov bool) { zulu.EnablePrefixMatching = ov }(zulu.EnablePrefixMatching) zulu.EnablePrefixMatching = tc.epm @@ -1687,8 +1751,8 @@ func TestCalledAs(t *testing.T) { return } - assertEqual(t, tc.call, called.Name()) - assertEqual(t, tc.want, called.CalledAs()) + testutil.AssertEqual(t, tc.call, called.Name()) + testutil.AssertEqual(t, tc.want, called.CalledAs()) }, ) } @@ -1699,8 +1763,8 @@ func TestFParseErrWhitelistBackwardCompatibility(t *testing.T) { c.Flags().Bool("boola", false, "a boolean flag", zflag.OptShorthand('a')) output, err := executeCommand(c, "c", "-a", "--unknown", "flag") - assertNotNilf(t, err, "expected unknown flag error") - assertContains(t, output, "unknown flag: --unknown") + testutil.AssertNotNilf(t, err, "expected unknown flag error") + testutil.AssertContains(t, output, "unknown flag: --unknown") } func TestFParseErrWhitelistSameCommand(t *testing.T) { @@ -1714,7 +1778,7 @@ func TestFParseErrWhitelistSameCommand(t *testing.T) { c.Flags().Bool("boola", false, "a boolean flag", zflag.OptShorthand('a')) _, err := executeCommand(c, "c", "-a", "--unknown", "flag") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestFParseErrWhitelistParentCommand(t *testing.T) { @@ -1735,8 +1799,8 @@ func TestFParseErrWhitelistParentCommand(t *testing.T) { root.AddCommand(c) output, err := executeCommand(root, "child", "-a", "--unknown", "flag") - assertNotNilf(t, err, "expected unknown flag error") - assertContains(t, output, "unknown flag: --unknown") + testutil.AssertNotNilf(t, err, "expected unknown flag error") + testutil.AssertContains(t, output, "unknown flag: --unknown") } func TestFParseErrWhitelistChildCommand(t *testing.T) { @@ -1757,7 +1821,7 @@ func TestFParseErrWhitelistChildCommand(t *testing.T) { root.AddCommand(c) _, err := executeCommand(root, "child", "-a", "--unknown", "flag") - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestFParseErrWhitelistSiblingCommand(t *testing.T) { @@ -1785,13 +1849,13 @@ func TestFParseErrWhitelistSiblingCommand(t *testing.T) { root.AddCommand(s) output, err := executeCommand(root, "sibling", "-b", "--unknown", "flag") - assertNotNilf(t, err, "expected unknown flag error") - assertContains(t, output, "unknown flag: --unknown") + testutil.AssertNotNilf(t, err, "expected unknown flag error") + testutil.AssertContains(t, output, "unknown flag: --unknown") } func TestContext(t *testing.T) { root := &zulu.Command{} - assertNotNilf(t, root.Context(), "expected root.Context() != nil") + testutil.AssertNotNilf(t, root.Context(), "expected root.Context() != nil") } func TestSetContext(t *testing.T) { @@ -1801,8 +1865,8 @@ func TestSetContext(t *testing.T) { RunE: func(cmd *zulu.Command, args []string) error { key := cmd.Context().Value(key) got, ok := key.(string) - assertEqualf(t, true, ok, "key not found in context") - assertEqual(t, val, got) + testutil.AssertEqualf(t, true, ok, "key not found in context") + testutil.AssertEqual(t, val, got) return nil }, } @@ -1811,7 +1875,7 @@ func TestSetContext(t *testing.T) { ctx := context.WithValue(context.Background(), key, val) root.SetContext(ctx) err := root.Execute() - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestSetContextPreRun(t *testing.T) { @@ -1819,7 +1883,6 @@ func TestSetContextPreRun(t *testing.T) { root := &zulu.Command{ Use: "root", PreRunE: func(cmd *zulu.Command, args []string) error { - //nolint:staticcheck // not necessary to create separate type for this ctx := context.WithValue(cmd.Context(), key, val) cmd.SetContext(ctx) @@ -1828,13 +1891,13 @@ func TestSetContextPreRun(t *testing.T) { RunE: func(cmd *zulu.Command, args []string) error { key := cmd.Context().Value(key) got, ok := key.(string) - assertEqualf(t, true, ok, "key not found in context") - assertEqual(t, val, got) + testutil.AssertEqualf(t, true, ok, "key not found in context") + testutil.AssertEqual(t, val, got) return nil }, } err := root.Execute() - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestSetContextPreRunOverwrite(t *testing.T) { @@ -1844,7 +1907,7 @@ func TestSetContextPreRunOverwrite(t *testing.T) { RunE: func(cmd *zulu.Command, args []string) error { key := cmd.Context().Value(key) _, ok := key.(string) - assertEqualf(t, false, ok, "key found in context") + testutil.AssertEqualf(t, false, ok, "key found in context") return nil }, } @@ -1853,7 +1916,7 @@ func TestSetContextPreRunOverwrite(t *testing.T) { ctx := context.WithValue(context.Background(), key, val) root.SetContext(ctx) err := root.ExecuteContext(context.Background()) - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestSetContextPersistentPreRun(t *testing.T) { @@ -1861,7 +1924,6 @@ func TestSetContextPersistentPreRun(t *testing.T) { root := &zulu.Command{ Use: "root", PersistentPreRunE: func(cmd *zulu.Command, args []string) error { - //nolint:staticcheck // not necessary to create separate type for this ctx := context.WithValue(cmd.Context(), key, val) cmd.SetContext(ctx) @@ -1873,15 +1935,15 @@ func TestSetContextPersistentPreRun(t *testing.T) { RunE: func(cmd *zulu.Command, args []string) error { key := cmd.Context().Value(key) got, ok := key.(string) - assertEqualf(t, true, ok, "key not found in context") - assertEqual(t, val, got) + testutil.AssertEqualf(t, true, ok, "key not found in context") + testutil.AssertEqual(t, val, got) return nil }, } root.AddCommand(child) root.SetArgs([]string{"child"}) err := root.Execute() - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") } func TestUsageTemplate(t *testing.T) { @@ -2088,33 +2150,20 @@ Use "root child [command] --help" for more information about a command. t.Parallel() for _, test := range tests { - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() var buf bytes.Buffer cmd := test.testCmd(&buf) err := cmd.Usage() - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") output := rmCarriageRet(buf.String()) - assertEqual(t, test.expectedUsage, output) + testutil.AssertEqual(t, test.expectedUsage, output) }) } } func TestFind(t *testing.T) { - var foo, bar string - root := &zulu.Command{ - Use: "root", - } - root.PersistentFlags().StringVar(&foo, "foo", "", "", zflag.OptShorthand('f')) - root.PersistentFlags().StringVar(&bar, "bar", "something", "", zflag.OptShorthand('b')) - - child := &zulu.Command{ - Use: "child", - } - root.AddCommand(child) - testCases := []struct { args []string expectedFoundArgs []string @@ -2176,9 +2225,22 @@ func TestFind(t *testing.T) { t.Parallel() for _, tc := range testCases { t.Run(fmt.Sprintf("%v", tc.args), func(t *testing.T) { + t.Parallel() + + root := &zulu.Command{ + Use: "root", + } + root.PersistentFlags().String("foo", "", "", zflag.OptShorthand('f')) + root.PersistentFlags().String("bar", "something", "", zflag.OptShorthand('b')) + + child := &zulu.Command{ + Use: "child", + } + root.AddCommand(child) + cmd, foundArgs, err := root.Find(tc.args) - assertNil(t, err) - assertEqualf(t, child, cmd, "Expected cmd to be child, but it was not") + testutil.AssertNil(t, err) + testutil.AssertEqualf(t, child, cmd, "Expected cmd to be child, but it was not") if !reflect.DeepEqual(tc.expectedFoundArgs, foundArgs) { t.Fatalf("Wrong args\nExpected: %v\nGot: %v", tc.expectedFoundArgs, foundArgs) @@ -2188,13 +2250,20 @@ func TestFind(t *testing.T) { } // adapted from https://github.com/spf13/cobra/pull/1632/files#diff-4c08781a1c6c69898cdd3a21c0c759d846fc32148e6b5aaf70ad2db146e9f145R2165 +// +//nolint:lll // can't cut it up func TestPadding(t *testing.T) { rootCmd := &zulu.Command{Use: "root", RunE: noopRun} childCmd := &zulu.Command{Use: "child", RunE: noopRun} longChildCmd := &zulu.Command{Use: "long-name-child-abcdefghijklmnopqrstuvwxyz", RunE: noopRun} - // For this test to be useful the hiddenChildCmd and deprecatedChildCmd commands need to have a longer `Use` field than the other commands. + // For this test to be useful the hiddenChildCmd and deprecatedChildCmd commands need to + // have a longer `Use` field than the other commands. hiddenChildCmd := &zulu.Command{Use: longChildCmd.Use + "-hidden", Hidden: true, RunE: noopRun} - deprecatedChildCmd := &zulu.Command{Use: longChildCmd.Use + "-deprecated", Deprecated: "deprecated", RunE: noopRun} + deprecatedChildCmd := &zulu.Command{ + Use: longChildCmd.Use + "-deprecated", + Deprecated: "deprecated", + RunE: noopRun, + } rootCmd.AddCommand(childCmd) rootCmd.AddCommand(longChildCmd) @@ -2208,10 +2277,10 @@ func TestPadding(t *testing.T) { childPadding := childCmd.Padding() longChildPadding := longChildCmd.Padding() - assertEqual(t, expectedUsePad, childPadding.Usage) - assertEqual(t, expectedUsePad, longChildPadding.Usage) - assertEqual(t, expectedPathPad, childPadding.CommandPath) - assertEqual(t, expectedPathPad, longChildPadding.CommandPath) - assertEqual(t, expectedNamePad, childPadding.Name) - assertEqual(t, expectedNamePad, longChildPadding.Name) + testutil.AssertEqual(t, expectedUsePad, childPadding.Usage) + testutil.AssertEqual(t, expectedUsePad, longChildPadding.Usage) + testutil.AssertEqual(t, expectedPathPad, childPadding.CommandPath) + testutil.AssertEqual(t, expectedPathPad, longChildPadding.CommandPath) + testutil.AssertEqual(t, expectedNamePad, childPadding.Name) + testutil.AssertEqual(t, expectedNamePad, longChildPadding.Name) } diff --git a/command_win.go b/command_win.go index cf302f3..617e721 100644 --- a/command_win.go +++ b/command_win.go @@ -11,9 +11,7 @@ import ( "github.com/inconshreveable/mousetrap" ) -var preExecHookFn = preExecHook - -func preExecHook(c *Command) { +func runMouseTrap(command *Command) { if MousetrapHelpText != "" && mousetrap.StartedByExplorer() { c.Print(MousetrapHelpText) if MousetrapDisplayDuration > 0 { diff --git a/completion-integrations/testprog/testprog.go b/completion-integrations/testprog/testprog.go index afa3572..1a1ad54 100644 --- a/completion-integrations/testprog/testprog.go +++ b/completion-integrations/testprog/testprog.go @@ -1,3 +1,4 @@ +//nolint:forbidigo // lots of fmt.println's called for testing, but it doesn't matter package main import ( @@ -27,15 +28,13 @@ func getCompsFilteredByPrefix(prefix string) []string { var rootCmd = &zulu.Command{ Use: "testprog", - RunE: func(cmd *zulu.Command, args []string) error { + RunE: func(_ *zulu.Command, _ []string) error { fmt.Println("rootCmd called") return nil }, } -// ====================================================== -// Set of commands that filter on the 'toComplete' prefix -// ====================================================== +// Set of commands that filter on the 'toComplete' prefix. var prefixCmd = &zulu.Command{ Use: "prefix", Short: "completions filtered on prefix", @@ -44,7 +43,7 @@ var prefixCmd = &zulu.Command{ var defaultCmdPrefix = &zulu.Command{ Use: "default", Short: "Directive: default", - ValidArgsFunction: func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + ValidArgsFunction: func(_ *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { return getCompsFilteredByPrefix(toComplete), zulu.ShellCompDirectiveDefault }, RunE: noopRun, @@ -92,9 +91,7 @@ var noFileNoSpaceCmdPrefix = &zulu.Command{ RunE: noopRun, } -// ====================================================== -// Set of commands that do not filter on prefix -// ====================================================== +// Set of commands that do not filter on prefix. var noPrefixCmd = &zulu.Command{ Use: "noprefix", Short: "completions NOT filtered on prefix", @@ -136,9 +133,7 @@ var defaultCmdNoPrefix = &zulu.Command{ RunE: noopRun, } -// ====================================================== -// Command that completes on file extension -// ====================================================== +// Command that completes on file extension. var fileExtCmdPrefix = &zulu.Command{ Use: "fileext", Short: "Directive: fileext", @@ -148,9 +143,7 @@ var fileExtCmdPrefix = &zulu.Command{ RunE: noopRun, } -// ====================================================== -// Command that completes on the directories within the current directory -// ====================================================== +// Command that completes on the directories within the current directory. var dirCmd = &zulu.Command{ Use: "dir", Short: "Directive: subdir", @@ -160,9 +153,7 @@ var dirCmd = &zulu.Command{ RunE: noopRun, } -// ====================================================== -// Command that completes on the directories within the 'dir' directory -// ====================================================== +// Command that completes on the directories within the 'dir' directory. var subDirCmd = &zulu.Command{ Use: "subdir", Short: "Directive: subdir", @@ -172,9 +163,7 @@ var subDirCmd = &zulu.Command{ RunE: noopRun, } -// ====================================================== -// Command that returns an error on completion -// ====================================================== +// Command that returns an error on completion. var errorCmd = &zulu.Command{ Use: "error", Short: "Directive: error", @@ -184,10 +173,7 @@ var errorCmd = &zulu.Command{ RunE: noopRun, } -// ====================================================== -// Command that wants an argument starting with a -- -// Such an argument is possible following a '--' -// ====================================================== +// Such an argument is possible following a '--'. var dashArgCmd = &zulu.Command{ Use: "dasharg", Short: "Wants argument --arg", @@ -197,16 +183,14 @@ var dashArgCmd = &zulu.Command{ RunE: noopRun, } -// ====================================================== // Command generates many completions. // It can be used to test performance. -// ====================================================== var manyCompsCmd = &zulu.Command{ Use: "manycomps", Short: "Outputs a thousand completions", ValidArgsFunction: func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { var comps []string - for i := 0; i < 1000; i++ { + for i := range 1000 { comps = append(comps, fmt.Sprintf("%[1]d-comp\tThis is comp %[1]d", i)) } return comps, zulu.ShellCompDirectiveDefault @@ -215,9 +199,15 @@ var manyCompsCmd = &zulu.Command{ } func setFlags() { - completionFunc := zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { - return []string{"firstComp\tthe first value", "secondComp\tthe second value", "forthComp"}, zulu.ShellCompDirectiveNoFileComp - }) + completionFunc := zulu.FlagOptCompletionFunc( + func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + return []string{ + "firstComp\tthe first value", + "secondComp\tthe second value", + "forthComp", + }, zulu.ShellCompDirectiveNoFileComp + }, + ) rootCmd.Flags().String("customComp", "", "test custom comp for flags", completionFunc) rootCmd.Flags().String("theme", "", "theme to use (located in /dir/THEMENAME/)", zulu.FlagOptDirname("dir")) diff --git a/completions.go b/completions.go index e176e4b..703ccb9 100644 --- a/completions.go +++ b/completions.go @@ -1,6 +1,7 @@ package zulu import ( + "errors" "fmt" "io" "log" @@ -21,10 +22,13 @@ const ( ShellCompNoDescRequestCmd = "__completeNoDesc" ) -// A global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. -var flagCompletionFunctions = map[*zflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} +type FlagCompletionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) -// Lock for reading and writing from flagCompletionFunctions +// flagCompletionFunctions contains a global map of flag completion functions. +// Make sure to use flagCompletionMutex before you try to read and write from it. +var flagCompletionFunctions = map[*zflag.Flag]FlagCompletionFn{} + +// Lock for reading and writing from flagCompletionFunctions. var flagCompletionMutex = &sync.RWMutex{} var logger *log.Logger @@ -80,7 +84,7 @@ const ( ShellCompDirectiveFilterDirs // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order - // in which the completions are provided + // in which the completions are provided. ShellCompDirectiveKeepOrder // =========================================================================== @@ -95,14 +99,14 @@ const ( ) const ( - // Constants for the completion command + // Constants for the completion command. compCmdName = "completion" compCmdDescFlagName = "descriptions" compCmdDescFlagDesc = "enable or disable completion descriptions" compCmdDescFlagDefault = true ) -// CompletionOptions are the options to control shell completion +// CompletionOptions are the options to control shell completion. type CompletionOptions struct { // DisableDefaultCmd prevents Zulu from creating a default 'completion' command DisableDefaultCmd bool @@ -118,19 +122,21 @@ type CompletionOptions struct { // NoFileCompletions can be used to disable file completion for commands that should // not trigger file completions. -func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { - return nil, ShellCompDirectiveNoFileComp +func NoFileCompletions() FlagCompletionFn { + return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { + return nil, ShellCompDirectiveNoFileComp + } } // FixedCompletions can be used to create a completion function which always // returns the same results. -func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { +func FixedCompletions(choices []string, directive ShellCompDirective) FlagCompletionFn { return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { return choices, directive } } -// ListDirectives returns a string listing the different directive enabled in the specified parameter +// ListDirectives returns a string listing the different directive enabled in the specified parameter. func (d ShellCompDirective) ListDirectives() string { var directives []string @@ -224,6 +230,7 @@ func (c *Command) initCompleteCmd(args []string) { } } +//nolint:gocognit,cyclop,gocyclo,funlen // todo refactor later func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) { // The last argument, which is not completely typed by the user, // should not be part of the list of arguments @@ -252,7 +259,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } if err != nil { // Unable to find the real command. E.g., someInvalidCmd - return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs) + return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) } finalCmd.ctx = c.ctx @@ -279,7 +286,10 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Parse the flags early, so we can check if required flags are set if err = finalCmd.ParseFlags(finalArgs); err != nil { - return finalCmd, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) + return finalCmd, + []string{}, + ShellCompDirectiveDefault, + fmt.Errorf("error while parsing flags from args %v: %s", finalArgs, err.Error()) } realArgCount := finalCmd.Flags().NArg() @@ -290,7 +300,8 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Error while attempting to parse flags if flagErr != nil { // If error type is flagCompError, and we don't want flagCompletion we should ignore the error - if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) { + var flagCompErr *flagCompError + if !(errors.As(flagErr, &flagCompErr) && !flagCompletion) { return finalCmd, []string{}, ShellCompDirectiveDefault, flagErr } } @@ -307,6 +318,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi finalArgs = finalCmd.Flags().Args() } + //nolint:nestif // todo refactor later if flag != nil && flagCompletion { // Check if we are completing a flag value subject to annotations if validExts, present := flag.Annotations[BashCompFilenameExt]; present { @@ -343,6 +355,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // When doing completion of a flag name, as soon as an argument starts with // a '-' we know it is a flag. We cannot use isFlagArg() here as it requires // the flag name to be complete + //nolint:nestif // todo refactor later if flag == nil && len(toComplete) > 0 && toComplete[0] == '-' && !strings.Contains(toComplete, "=") && flagCompletion { // First check for required flags completions = completeRequireFlags(finalCmd, toComplete) @@ -451,7 +464,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } // Find the completion function for the flag or command - var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + var completionFn FlagCompletionFn if flag != nil && flagCompletion { flagCompletionMutex.RLock() completionFn = flagCompletionFunctions[flag] @@ -538,6 +551,7 @@ func completeRequireFlags(finalCmd *Command, toComplete string) []string { return completions } +//nolint:gocognit // old function, needs to be done later func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*zflag.Flag, []string, string, error) { if finalCmd.DisableFlagParsing { // We only do flag completion if we are allowed to parse flags @@ -554,48 +568,48 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*z // a '-' we know it is a flag. We cannot use isFlagArg() here as that function // requires the flag name to be complete if len(lastArg) > 0 && lastArg[0] == '-' { - if index := strings.Index(lastArg, "="); index >= 0 { - // Flag with an = - if strings.HasPrefix(lastArg[:index], "--") { - // Flag has full name - flagName = lastArg[2:index] - } else { - // Flag is shorthand - // We have to get the last shorthand flag name - // e.g. `-asd` => d to provide the correct completion - // https://github.com/spf13/cobra/issues/1257 - flagName = lastArg[index-1 : index] - } - lastArg = lastArg[index+1:] - flagWithEqual = true - } else { + index := strings.Index(lastArg, "=") + if index == -1 { // Normal flag completion return nil, args, lastArg, nil } - } - if len(flagName) == 0 { - if len(args) > 0 { - prevArg := args[len(args)-1] - if isFlagArg(prevArg) { - // Only consider the case where the flag does not contain an =. - // If the flag contains an = it means it has already been fully processed, - // so we don't need to deal with it here. - if index := strings.Index(prevArg, "="); index < 0 { - if strings.HasPrefix(prevArg, "--") { - // Flag has full name - flagName = prevArg[2:] - } else { - // Flag is shorthand - // We have to get the last shorthand flag name - // e.g. `-asd` => d to provide the correct completion - // https://github.com/spf13/cobra/issues/1257 - flagName = prevArg[len(prevArg)-1:] - } - // Remove the uncompleted flag or else there could be an error created - // for an invalid value for that flag - trimmedArgs = args[:len(args)-1] + // Flag with an = + if strings.HasPrefix(lastArg[:index], "--") { + // Flag has full name + flagName = lastArg[2:index] + } else { + // Flag is shorthand + // We have to get the last shorthand flag name + // e.g. `-asd` => d to provide the correct completion + // https://github.com/spf13/cobra/issues/1257 + flagName = lastArg[index-1 : index] + } + lastArg = lastArg[index+1:] + flagWithEqual = true + } + + //nolint:nestif // todo refactor later + if len(flagName) == 0 && len(args) > 0 { + prevArg := args[len(args)-1] + if isFlagArg(prevArg) { + // Only consider the case where the flag does not contain an =. + // If the flag contains an = it means it has already been fully processed, + // so we don't need to deal with it here. + if index := strings.Index(prevArg, "="); index < 0 { + if strings.HasPrefix(prevArg, "--") { + // Flag has full name + flagName = prevArg[2:] + } else { + // Flag is shorthand + // We have to get the last shorthand flag name + // e.g. `-asd` => d to provide the correct completion + // https://github.com/spf13/cobra/issues/1257 + flagName = prevArg[len(prevArg)-1:] } + // Remove the uncompleted flag or else there could be an error created + // for an invalid value for that flag + trimmedArgs = args[:len(args)-1] } } } @@ -641,7 +655,12 @@ func (c *Command) InitDefaultCompletionCmd() { } } - long, err := template.ParseFromFile(tmplFS, "templates/usage_completion_root.txt.gotmpl", map[string]string{"CMDName": c.Root().Name()}, templateFuncs) + long, err := template.ParseFromFile( + tmplFS, + "templates/usage_completion_root.txt.gotmpl", + map[string]string{"CMDName": c.Root().Name()}, + templateFuncs, + ) if err != nil { panic(err) } @@ -651,34 +670,64 @@ func (c *Command) InitDefaultCompletionCmd() { Short: "Generate the autocompletion script for the specified shell", Long: long, Args: NoArgs, - ValidArgsFunction: NoFileCompletions, + ValidArgsFunction: NoFileCompletions(), Hidden: c.CompletionOptions.HiddenDefaultCmd, } c.AddCommand(completionCmd) out := c.OutOrStdout() includeDescriptions := !c.CompletionOptions.DisableDescriptions - bash := c.createCompletionCommand("bash", "templates/usage_completion_bash.txt.gotmpl", &includeDescriptions, func(cmd *Command, args []string) error { - return cmd.Root().GenBashCompletion(out, includeDescriptions) - }) - - zsh := c.createCompletionCommand("zsh", "templates/usage_completion_zsh.txt.gotmpl", &includeDescriptions, func(cmd *Command, args []string) error { - return cmd.Root().GenZshCompletion(out, includeDescriptions) - }) - - fish := c.createCompletionCommand("fish", "templates/usage_completion_fish.txt.gotmpl", &includeDescriptions, func(cmd *Command, args []string) error { - return cmd.Root().GenFishCompletion(out, includeDescriptions) - }) - - powershell := c.createCompletionCommand("powershell", "templates/usage_completion_pwsh.txt.gotmpl", &includeDescriptions, func(cmd *Command, args []string) error { - return cmd.Root().GenPowershellCompletion(out, includeDescriptions) - }) + bash := c.createCompletionCommand( + "bash", + "templates/usage_completion_bash.txt.gotmpl", + &includeDescriptions, + func(cmd *Command, args []string) error { + return cmd.Root().GenBashCompletion(out, includeDescriptions) + }, + ) + + zsh := c.createCompletionCommand( + "zsh", + "templates/usage_completion_zsh.txt.gotmpl", + &includeDescriptions, + func(cmd *Command, args []string) error { + return cmd.Root().GenZshCompletion(out, includeDescriptions) + }, + ) + + fish := c.createCompletionCommand( + "fish", + "templates/usage_completion_fish.txt.gotmpl", + &includeDescriptions, + func(cmd *Command, args []string) error { + return cmd.Root().GenFishCompletion(out, includeDescriptions) + }, + ) + + powershell := c.createCompletionCommand( + "powershell", + "templates/usage_completion_pwsh.txt.gotmpl", + &includeDescriptions, + func(cmd *Command, args []string) error { + return cmd.Root().GenPowershellCompletion(out, includeDescriptions) + }, + ) completionCmd.AddCommand(bash, zsh, fish, powershell) } -func (c *Command) createCompletionCommand(shellName string, usageTemplate string, includeDescriptions *bool, runFn HookFuncE) *Command { - long, err := template.ParseFromFile(tmplFS, usageTemplate, map[string]string{"CMDName": c.Root().Name()}, templateFuncs) +func (c *Command) createCompletionCommand( + shellName string, + usageTemplate string, + includeDescriptions *bool, + runFn HookFuncE, +) *Command { + long, err := template.ParseFromFile( + tmplFS, + usageTemplate, + map[string]string{"CMDName": c.Root().Name()}, + templateFuncs, + ) if err != nil { panic(err) } @@ -688,35 +737,41 @@ func (c *Command) createCompletionCommand(shellName string, usageTemplate string Short: fmt.Sprintf("Generate the autocompletion script for %s", shellName), Long: long, Args: NoArgs, - ValidArgsFunction: NoFileCompletions, + ValidArgsFunction: NoFileCompletions(), RunE: runFn, } haveDescriptionsFlag := !c.CompletionOptions.DisableDescriptionsFlag && !c.CompletionOptions.DisableDescriptions if haveDescriptionsFlag { - completionCMD.Flags().BoolVar(includeDescriptions, compCmdDescFlagName, compCmdDescFlagDefault, compCmdDescFlagDesc, zflag.OptAddNegative()) + completionCMD.Flags().BoolVar( + includeDescriptions, + compCmdDescFlagName, + compCmdDescFlagDefault, + compCmdDescFlagDesc, + zflag.OptAddNegative(), + ) } return completionCMD } func findFlag(cmd *Command, name string) *zflag.Flag { - flagSet := cmd.Flags() - if len(name) == 1 { - // First convert the short flag into a long flag - // as the cmd.Flag() search only accepts long flags - if short := flagSet.ShorthandLookupStr(name); short != nil { - name = short.Name - } else { - set := cmd.InheritedFlags() - if short = set.ShorthandLookupStr(name); short != nil { - name = short.Name - } else { - return nil - } - } + if len(name) != 1 { + return cmd.Flag(name) + } + + // First convert the short flag into a long flag + // as the cmd.Flag() search only accepts long flags + short := cmd.Flags().ShorthandLookupStr(name) + if short == nil { + short = cmd.InheritedFlags().ShorthandLookupStr(name) } - return cmd.Flag(name) + + if short != nil { + return cmd.Flag(short.Name) + } + + return nil } // CompLogger gets or creates a logger that prints to stderr or the completion log file. @@ -724,6 +779,7 @@ func findFlag(cmd *Command, name string) *zflag.Flag { // to true. The logs can be optionally output to a file by setting `BASH_COMP_DEBUG_FILE` to // a file location. func CompLogger() *log.Logger { + //nolint:nestif // todo refactor later if logger == nil { var f io.Writer debugFile := os.Getenv("BASH_COMP_DEBUG_FILE") @@ -756,7 +812,7 @@ func genTemplateCompletion(buf io.Writer, templateFile string, name string, incl nameForVar = strings.ReplaceAll(nameForVar, "-", "_") nameForVar = strings.ReplaceAll(nameForVar, ":", "_") - res, err := template.ParseFromFile(tmplFS, templateFile, map[string]interface{}{ + res, err := template.ParseFromFile(tmplFS, templateFile, map[string]any{ "CMDVarName": nameForVar, "CMDName": name, "CompletionCommand": compCmd, diff --git a/completions_test.go b/completions_test.go index 9733372..fd89f5c 100644 --- a/completions_test.go +++ b/completions_test.go @@ -8,6 +8,7 @@ import ( "github.com/zulucmd/zflag/v2" "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func validArgsFunc(_ *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { @@ -73,7 +74,7 @@ func TestCmdNameCompletionInGo(t *testing.T) { // Test that sub-command names are completed output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "aliased", @@ -84,33 +85,33 @@ func TestCmdNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names are completed with prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "s") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "secondChild", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that even with no valid sub-command matches, hidden, deprecated and // aliases are not completed output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "test") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names are completed with description output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "aliased\tA command with aliases", @@ -121,7 +122,7 @@ func TestCmdNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestNoCmdNameCompletionInGo(t *testing.T) { @@ -151,17 +152,17 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { // Test that sub-command names are not completed if there is an argument already output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd1", "arg1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names are not completed if a local non-persistent flag is present output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command nonPersistentFlag.Changed = false @@ -169,14 +170,15 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) - // Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true + // Test that sub-command names are completed if a local non-persistent flag is present + // and TraverseChildren is set to true // set TraverseChildren to true on the root cmd rootCmd.TraverseChildren = true output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--localroot", "value", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset TraverseChildren for next command rootCmd.TraverseChildren = false @@ -187,14 +189,23 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names from a child cmd are completed if a local non-persistent flag is present // and TraverseChildren is set to true on the root cmd rootCmd.TraverseChildren = true - output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "") - assertNilf(t, err, "Unexpected error: %v", err) + output, err = executeCommand( + rootCmd, + zulu.ShellCompNoDescRequestCmd, + "--localroot", + "value", + "childCmd1", + "--nonPersistent", + "value", + "", + ) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset TraverseChildren for next command rootCmd.TraverseChildren = false // Reset the flag for the next command @@ -205,22 +216,22 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that we don't use Traverse when we shouldn't. // This command should not return a completion since the command line is invalid without TraverseChildren. output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names are not completed if a local non-persistent short flag is present output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command nonPersistentFlag.Changed = false @@ -228,11 +239,11 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names are completed with a persistent flag output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command persistentFlag.Changed = false @@ -241,11 +252,11 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that sub-command names are completed with a persistent short flag output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command persistentFlag.Changed = false @@ -254,7 +265,7 @@ func TestNoCmdNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsCompletionInGo(t *testing.T) { @@ -266,7 +277,7 @@ func TestValidArgsCompletionInGo(t *testing.T) { // Test that validArgs are completed output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one", @@ -275,28 +286,28 @@ func TestValidArgsCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that validArgs are completed with prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "o") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "one", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that validArgs don't repeat output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "one", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsAndCmdCompletionInGo(t *testing.T) { @@ -315,7 +326,7 @@ func TestValidArgsAndCmdCompletionInGo(t *testing.T) { // Test that both sub-commands and validArgs are completed output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "completion", @@ -326,11 +337,11 @@ func TestValidArgsAndCmdCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that both sub-commands and validArgs are completed with prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "thechild", @@ -338,7 +349,7 @@ func TestValidArgsAndCmdCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { @@ -358,7 +369,7 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { // Test that both sub-commands and validArgsFunction are completed output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "completion", @@ -369,11 +380,11 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that both sub-commands and validArgs are completed with prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "thechild", @@ -381,11 +392,11 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that both sub-commands and validArgs are completed with description output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "thechild\tThe child command", @@ -393,7 +404,7 @@ func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagNameCompletionInGo(t *testing.T) { @@ -414,7 +425,7 @@ func TestFlagNameCompletionInGo(t *testing.T) { // Test that flag names are not shown if the user has not given the '-' prefix output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "childCmd", @@ -423,11 +434,11 @@ func TestFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are completed output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--first", @@ -439,22 +450,22 @@ func TestFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are completed when a prefix is given output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--f") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--first", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are completed in a sub-cmd output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--second", @@ -467,7 +478,7 @@ func TestFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagNameCompletionInGoWithDesc(t *testing.T) { @@ -489,7 +500,7 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { // Test that flag names are not shown if the user has not given the '-' prefix output, err := executeCommand(rootCmd, zulu.ShellCompRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "childCmd\tfirst command", @@ -498,11 +509,11 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are completed output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--first\tfirst flag", @@ -514,22 +525,22 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are completed when a prefix is given output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "--f") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--first\tfirst flag", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are completed in a sub-cmd output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "childCmd", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--second\tsecond flag", @@ -542,7 +553,7 @@ func TestFlagNameCompletionInGoWithDesc(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagNameCompletionRepeat(t *testing.T) { @@ -568,7 +579,7 @@ func TestFlagNameCompletionRepeat(t *testing.T) { // Test that flag names are not repeated unless they are an array or slice output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--first", "1", "--") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command firstFlag.Changed = false @@ -580,11 +591,11 @@ func TestFlagNameCompletionRepeat(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are not repeated unless they are an array or slice output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command firstFlag.Changed = false secondFlag.Changed = false @@ -596,29 +607,43 @@ func TestFlagNameCompletionRepeat(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are not repeated unless they are an array or slice - output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--bslice", "true", "--") - assertNilf(t, err, "Unexpected error: %v", err) + output, err = executeCommand( + rootCmd, + zulu.ShellCompNoDescRequestCmd, + "--slice", + "1", + "--slice=2", + "--bslice", + "true", + "--", + ) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command sliceFlag.Changed = false bsliceFlag.Changed = false - expected = strings.Join([]string{ - "--bslice", - "--first", - "--help", - "--second", - "--slice", - ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + expected = strings.Join( + []string{ + "--bslice", + "--first", + "--help", + "--second", + "--slice", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", + "", + }, + "\n", + ) - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are not repeated unless they are an array or slice, using shortname output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command sliceFlag.Changed = false @@ -636,11 +661,11 @@ func TestFlagNameCompletionRepeat(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that flag names are not repeated unless they are an array or slice, using shortname with prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command sliceFlag.Changed = false @@ -648,7 +673,7 @@ func TestFlagNameCompletionRepeat(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestRequiredFlagNameCompletionInGo(t *testing.T) { @@ -669,17 +694,34 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { rootCmd.Flags().Int("requiredFlag", -1, "required flag", zflag.OptShorthand('r'), zflag.OptRequired()) requiredFlag := rootCmd.Flags().Lookup("requiredFlag") - rootCmd.PersistentFlags().Int("requiredPersistent", -1, "required persistent", zflag.OptShorthand('p'), zflag.OptRequired()) + rootCmd.PersistentFlags().Int( + "requiredPersistent", + -1, + "required persistent", + zflag.OptShorthand('p'), + zflag.OptRequired(), + ) requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent") rootCmd.Flags().String("release", "", "Release name", zflag.OptShorthand('R')) - childCmd.Flags().Bool("subRequired", false, "sub required flag", zflag.OptShorthand('s'), zflag.OptRequired()) - childCmd.Flags().Bool("subNotRequired", false, "sub not required flag", zflag.OptShorthand('n')) + childCmd.Flags().Bool( + "subRequired", + false, + "sub required flag", + zflag.OptShorthand('s'), + zflag.OptRequired(), + ) + childCmd.Flags().Bool( + "subNotRequired", + false, + "sub not required flag", + zflag.OptShorthand('n'), + ) // Test that a required flag is suggested even without the - prefix output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "childCmd", @@ -693,11 +735,11 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that a required flag is suggested without other flags when using the '-' prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--requiredFlag", @@ -707,22 +749,22 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that if no required flag matches, the normal flags are suggested output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--relea") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--release", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test required flags for sub-commands output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--requiredPersistent", @@ -733,10 +775,10 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--requiredPersistent", @@ -746,21 +788,21 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd", "--subNot") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--subNotRequired", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that when a required flag is present, it is not suggested anymore output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--requiredFlag", "1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command requiredFlag.Changed = false @@ -771,11 +813,11 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that when a persistent required flag is present, it is not suggested anymore output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flag for the next command requiredPersistent.Changed = false @@ -789,11 +831,19 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that when all required flags are present, normal completion is done - output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "") - assertNilf(t, err, "Unexpected error: %v", err) + output, err = executeCommand( + rootCmd, + zulu.ShellCompNoDescRequestCmd, + "--requiredFlag", + "1", + "--requiredPersistent", + "1", + "", + ) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Reset the flags for the next command requiredFlag.Changed = false requiredPersistent.Changed = false @@ -803,7 +853,7 @@ func TestRequiredFlagNameCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagFileExtFilterCompletionInGo(t *testing.T) { @@ -813,87 +863,111 @@ func TestFlagFileExtFilterCompletionInGo(t *testing.T) { } // No extensions. Should be ignored. - rootCmd.Flags().String("file", "", "file flag", zflag.OptShorthand('f'), zulu.FlagOptFilename()) + rootCmd.Flags().String( + "file", + "", + "file flag", + zflag.OptShorthand('f'), + zulu.FlagOptFilename(), + ) // Single extension - rootCmd.Flags().String("log", "", "log flag", zflag.OptShorthand('l'), zulu.FlagOptFilename("log")) + rootCmd.Flags().String( + "log", + "", + "log flag", + zflag.OptShorthand('l'), + zulu.FlagOptFilename("log"), + ) // Multiple extensions - rootCmd.Flags().String("yaml", "", "yaml flag", zflag.OptShorthand('y'), zulu.FlagOptFilename("yaml", "yml")) + rootCmd.Flags().String( + "yaml", + "", + "yaml flag", + zflag.OptShorthand('y'), + zulu.FlagOptFilename("yaml", "yml"), + ) // Directly using annotation - rootCmd.Flags().String("text", "", "text flag", zflag.OptShorthand('t'), zflag.OptAnnotation(zulu.BashCompFilenameExt, []string{"txt"})) + rootCmd.Flags().String( + "text", + "", + "text flag", + zflag.OptShorthand('t'), + zflag.OptAnnotation(zulu.BashCompFilenameExt, []string{"txt"}), + ) // Test that the completion logic returns the proper info for the completion // script to handle the file filtering output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--file", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--log", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "log", ":8", "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--yaml", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "yaml", "yml", ":8", "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--yaml=") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "yaml", "yml", ":8", "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-y", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "yaml", "yml", ":8", "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-y=") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "yaml", "yml", ":8", "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--text", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "txt", ":8", "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagDirFilterCompletionInGo(t *testing.T) { @@ -903,90 +977,108 @@ func TestFlagDirFilterCompletionInGo(t *testing.T) { } // Filter directories - rootCmd.Flags().String("dir", "", "dir flag", zflag.OptShorthand('d'), zulu.FlagOptDirname()) + rootCmd.Flags().String( + "dir", + "", + "dir flag", + zflag.OptShorthand('d'), + zulu.FlagOptDirname(), + ) // Filter directories within a directory - rootCmd.Flags().String("subdir", "", "subdir", zflag.OptShorthand('s'), zulu.FlagOptDirname("themes")) + rootCmd.Flags().String( + "subdir", + "", + "subdir", + zflag.OptShorthand('s'), + zulu.FlagOptDirname("themes"), + ) // Multiple directory specification get ignored - rootCmd.Flags().String("manydir", "", "manydir", zflag.OptShorthand('m'), zflag.OptAnnotation(zulu.BashCompSubdirsInDir, []string{"themes", "colors"})) + rootCmd.Flags().String( + "manydir", + "", + "manydir", + zflag.OptShorthand('m'), + zflag.OptAnnotation(zulu.BashCompSubdirsInDir, []string{"themes", "colors"}), + ) // Test that the completion logic returns the proper info for the completion // script to handle the directory filtering output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--dir", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-d", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--subdir", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "themes", ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--subdir=") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "themes", ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-s", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "themes", ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-s=") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "themes", ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--manydir", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":16", "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncCmdContext(t *testing.T) { - validArgsFunc := func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + validArgsFuncCtx := func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { ctx := cmd.Context() - assertNotNilf(t, ctx, "Received nil context in completion func") - assertEqualf(t, "123", ctx.Value("testKey"), "Received invalid context") + testutil.AssertNotNilf(t, ctx, "Received nil context in completion func") + testutil.AssertEqualf(t, "123", ctx.Value("testKey"), "Received invalid context") return nil, zulu.ShellCompDirectiveDefault } @@ -997,7 +1089,7 @@ func TestValidArgsFuncCmdContext(t *testing.T) { } childCmd := &zulu.Command{ Use: "childCmd", - ValidArgsFunction: validArgsFunc, + ValidArgsFunction: validArgsFuncCtx, RunE: noopRun, } rootCmd.AddCommand(childCmd) @@ -1007,13 +1099,13 @@ func TestValidArgsFuncCmdContext(t *testing.T) { // Test completing an empty string on the childCmd _, output, err := executeCommandWithContextC(ctx, rootCmd, zulu.ShellCompNoDescRequestCmd, "childCmd", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncSingleCmd(t *testing.T) { @@ -1025,7 +1117,7 @@ func TestValidArgsFuncSingleCmd(t *testing.T) { // Test completing an empty string output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one", @@ -1033,18 +1125,18 @@ func TestValidArgsFuncSingleCmd(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with a prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "two", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) { @@ -1061,13 +1153,13 @@ func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) { // Check completing with wrong number of args output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "unexpectedArg", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncChildCmds(t *testing.T) { @@ -1086,7 +1178,7 @@ func TestValidArgsFuncChildCmds(t *testing.T) { // Test completion of first sub-command with empty argument output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one", @@ -1094,32 +1186,32 @@ func TestValidArgsFuncChildCmds(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completion of first sub-command with a prefix to complete output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child1", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "two", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with wrong number of args output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completion of second sub-command with empty argument output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child2", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "three", @@ -1127,27 +1219,27 @@ func TestValidArgsFuncChildCmds(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child2", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "three", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with wrong number of args output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncAliases(t *testing.T) { @@ -1162,7 +1254,7 @@ func TestValidArgsFuncAliases(t *testing.T) { // Test completion of first sub-command with empty argument output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "son", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one", @@ -1170,28 +1262,28 @@ func TestValidArgsFuncAliases(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completion of first sub-command with a prefix to complete output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "daughter", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "two", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with wrong number of args output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestCompleteNoDesCmdInZshScript(t *testing.T) { @@ -1204,10 +1296,10 @@ func TestCompleteNoDesCmdInZshScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenZshCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenZshCompletion(buf, false)) output := buf.String() - assertContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompNoDescRequestCmd) } func TestCompleteCmdInZshScript(t *testing.T) { @@ -1220,11 +1312,11 @@ func TestCompleteCmdInZshScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenZshCompletion(buf, true)) + testutil.AssertNil(t, rootCmd.GenZshCompletion(buf, true)) output := buf.String() - assertContains(t, output, zulu.ShellCompRequestCmd+" ") - assertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompRequestCmd+" ") + testutil.AssertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) } func TestFlagCompletionInGo(t *testing.T) { @@ -1232,8 +1324,16 @@ func TestFlagCompletionInGo(t *testing.T) { Use: "root", RunE: noopRun, } - rootCmd.Flags().Int("introot", -1, "help message for flag introot", zflag.OptShorthand('i'), - zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + rootCmd.Flags().Int( + "introot", + -1, + "help message for flag introot", + zflag.OptShorthand('i'), + zulu.FlagOptCompletionFunc(func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { completions := make([]string, 0) for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { if strings.HasPrefix(comp, toComplete) { @@ -1243,8 +1343,15 @@ func TestFlagCompletionInGo(t *testing.T) { return completions, zulu.ShellCompDirectiveDefault }), ) - rootCmd.Flags().String("filename", "", "Enter a filename", - zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + rootCmd.Flags().String( + "filename", + "", + "Enter a filename", + zulu.FlagOptCompletionFunc(func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { completions := make([]string, 0) for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { if strings.HasPrefix(comp, toComplete) { @@ -1257,7 +1364,7 @@ func TestFlagCompletionInGo(t *testing.T) { // Test completing an empty string output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--introot", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "1", @@ -1266,11 +1373,11 @@ func TestFlagCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with a prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--introot", "1") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "1", @@ -1278,32 +1385,41 @@ func TestFlagCompletionInGo(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completing an empty string output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--filename", "") - assertNilf(t, err, "Unexpected error: %v", err) - - expected = strings.Join([]string{ - "file.yaml", - "myfile.json", - "file.xml", - ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + testutil.AssertNilf(t, err, "Unexpected error: %v", err) + + expected = strings.Join( + []string{ + "file.yaml", + "myfile.json", + "file.xml", + ":6", + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", + "", + }, + "\n", + ) - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with a prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "--filename", "f") - assertNilf(t, err, "Unexpected error: %v", err) - - expected = strings.Join([]string{ - "file.yaml", - "file.xml", - ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + testutil.AssertNilf(t, err, "Unexpected error: %v", err) + + expected = strings.Join( + []string{ + "file.yaml", + "file.xml", + ":6", + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", + ""}, + "\n", + ) - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { @@ -1322,7 +1438,7 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { // Test completion of first sub-command with empty argument output, err := executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one\tThe first", @@ -1330,32 +1446,32 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completion of first sub-command with a prefix to complete output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child1", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "two\tThe second", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with wrong number of args output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child1", "unexpectedArg", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completion of second sub-command with empty argument output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child2", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "three\tThe third", @@ -1363,27 +1479,27 @@ func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child2", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "three\tThe third", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with wrong number of args output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child2", "unexpectedArg", "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { @@ -1403,14 +1519,18 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { rootCmd.AddCommand(childCmd, childCmd2) childCmd.Flags().Bool("bool", false, "test bool flag") childCmd.Flags().String("string", "", "test string flag", - zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + zulu.FlagOptCompletionFunc(func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return []string{"myval"}, zulu.ShellCompDirectiveDefault }), ) // Test flag completion with no argument output, err := executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "--bool\ttest bool flag", @@ -1419,11 +1539,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that no flags are completed after the -- arg output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1431,11 +1551,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that no flags are completed after the -- arg with a flag set output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--bool", "--", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1443,14 +1563,14 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // set Interspersed to false which means that no flags should be completed after the first arg childCmd.Flags().SetInterspersed(false) // Test that no flags are completed after the first arg output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "arg", "--") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1458,11 +1578,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that no flags are completed after the fist arg with a flag set output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--string", "t", "arg", "--") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1470,11 +1590,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that args are still completed after -- output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1482,11 +1602,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that args are still completed even if flagname with ValidArgsFunction exists output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--", "--string", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1494,11 +1614,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that args are still completed even if flagname with ValidArgsFunction exists output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child2", "--", "a") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "arg1", @@ -1506,11 +1626,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that --validarg is not parsed as flag after -- output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--", "--validarg", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1518,11 +1638,11 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that --validarg is not parsed as flag after an arg output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "arg", "--validarg", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1530,28 +1650,36 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that --validarg is added to args for the ValidArgsFunction - childCmd.ValidArgsFunction = func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + childCmd.ValidArgsFunction = func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return args, zulu.ShellCompDirectiveDefault } output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--", "--validarg", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check that --validarg is added to args for the ValidArgsFunction and toComplete is also set correctly - childCmd.ValidArgsFunction = func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + childCmd.ValidArgsFunction = func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return append(args, toComplete), zulu.ShellCompDirectiveDefault } output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--", "--validarg", "--toComp=ab") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "--validarg", @@ -1559,7 +1687,7 @@ func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) { @@ -1573,7 +1701,11 @@ func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) { } childCmd.Flags().Bool("bool", false, "test bool flag") childCmd.Flags().String("string", "", "test string flag", - zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + zulu.FlagOptCompletionFunc(func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return []string{"myval"}, zulu.ShellCompDirectiveDefault }), ) @@ -1584,14 +1716,14 @@ func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) { // Test that flag completion works for the subcmd output, err := executeCommand(rootCmd, zulu.ShellCompRequestCmd, "child", "--string", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "myval", ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFlagCompletionInGoWithDesc(t *testing.T) { @@ -1600,7 +1732,11 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { RunE: noopRun, } rootCmd.Flags().Int("introot", -1, "help message for flag introot", zflag.OptShorthand('i'), - zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + zulu.FlagOptCompletionFunc(func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { completions := []string{} for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { if strings.HasPrefix(comp, toComplete) { @@ -1611,7 +1747,11 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { }), ) rootCmd.Flags().String("filename", "", "Enter a filename", - zulu.FlagOptCompletionFunc(func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + zulu.FlagOptCompletionFunc(func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { completions := []string{} for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { if strings.HasPrefix(comp, toComplete) { @@ -1624,7 +1764,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { // Test completing an empty string output, err := executeCommand(rootCmd, zulu.ShellCompRequestCmd, "--introot", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "1\tThe first", @@ -1633,11 +1773,11 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with a prefix output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "--introot", "1") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "1\tThe first", @@ -1645,24 +1785,29 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { ":0", "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test completing an empty string output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "--filename", "") - assertNilf(t, err, "Unexpected error: %v", err) - - expected = strings.Join([]string{ - "file.yaml\tYAML format", - "myfile.json\tJSON format", - "file.xml\tXML format", - ":6", - "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") + testutil.AssertNilf(t, err, "Unexpected error: %v", err) + + expected = strings.Join( + []string{ + "file.yaml\tYAML format", + "myfile.json\tJSON format", + "file.xml\tXML format", + ":6", + "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", + "", + }, + "\n", + ) - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with a prefix output, err = executeCommand(rootCmd, zulu.ShellCompRequestCmd, "--filename", "f") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "file.yaml\tYAML format", @@ -1670,7 +1815,7 @@ func TestFlagCompletionInGoWithDesc(t *testing.T) { ":6", "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestValidArgsNotValidArgsFunc(t *testing.T) { @@ -1686,7 +1831,7 @@ func TestValidArgsNotValidArgsFunc(t *testing.T) { // Test that if both ValidArgs and ValidArgsFunction are present // only ValidArgs is considered output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one", @@ -1694,18 +1839,18 @@ func TestValidArgsNotValidArgsFunc(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Check completing with a prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "two", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestArgAliasesCompletionInGo(t *testing.T) { @@ -1719,7 +1864,7 @@ func TestArgAliasesCompletionInGo(t *testing.T) { // Test that argaliases are not completed when there are validargs that match output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "one", @@ -1728,11 +1873,11 @@ func TestArgAliasesCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that argaliases are not completed when there are validargs that match using a prefix output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "t") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "two", @@ -1740,18 +1885,18 @@ func TestArgAliasesCompletionInGo(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that argaliases are completed when there are no validargs that match output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "tr") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "trois", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestCompleteHelp(t *testing.T) { @@ -1774,7 +1919,7 @@ func TestCompleteHelp(t *testing.T) { // Test that completion includes the help command output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "child1", @@ -1784,11 +1929,11 @@ func TestCompleteHelp(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test sub-commands are completed on first level of help command output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "help", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "child1", @@ -1798,18 +1943,18 @@ func TestCompleteHelp(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test sub-commands are completed on first level of help command output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "help", "child1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "child3", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func removeCompCmd(rootCmd *zulu.Command) { @@ -1830,9 +1975,14 @@ func TestDefaultCompletionCmd(t *testing.T) { } // Test that no completion command is created if there are not other sub-commands - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) for _, cmd := range rootCmd.Commands() { - assertNotEqualf(t, zulu.CompCmdName, cmd.Name(), "Should not have a 'completion' command when there are no other sub-commands of root") + testutil.AssertNotEqualf( + t, + zulu.CompCmdName, + cmd.Name(), + "Should not have a 'completion' command when there are no other sub-commands of root", + ) } subCmd := &zulu.Command{ @@ -1843,41 +1993,51 @@ func TestDefaultCompletionCmd(t *testing.T) { // Test that a completion command is created if there are other sub-commands found := false - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) for _, cmd := range rootCmd.Commands() { if cmd.Name() == zulu.CompCmdName { found = true break } } - assertEqualf(t, true, found, "Should have a 'completion' command when there are other sub-commands of root") + testutil.AssertEqualf( + t, + true, + found, + "Should have a 'completion' command when there are other sub-commands of root", + ) // Remove completion command for the next test removeCompCmd(rootCmd) // Test that the default completion command can be disabled rootCmd.CompletionOptions.DisableDefaultCmd = true - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) for _, cmd := range rootCmd.Commands() { - assertNotEqualf(t, zulu.CompCmdName, cmd.Name(), "Should not have a 'completion' command when the feature is disabled") + testutil.AssertNotEqualf( + t, + zulu.CompCmdName, + cmd.Name(), + "Should not have a 'completion' command when the feature is disabled", + ) } // Re-enable for next test rootCmd.CompletionOptions.DisableDefaultCmd = false // Test that completion descriptions are enabled by default output, err := executeCommand(rootCmd, zulu.CompCmdName, "zsh") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) - assertContains(t, output, zulu.ShellCompRequestCmd+" ") - assertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompRequestCmd+" ") + testutil.AssertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) // Remove completion command for the next test removeCompCmd(rootCmd) // Test that completion descriptions can be disabled completely rootCmd.CompletionOptions.DisableDescriptions = true output, err = executeCommand(rootCmd, zulu.CompCmdName, "zsh") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) - assertContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompNoDescRequestCmd) // Re-enable for next test rootCmd.CompletionOptions.DisableDescriptions = false // Remove completion command for the next test @@ -1885,24 +2045,24 @@ func TestDefaultCompletionCmd(t *testing.T) { var compCmd *zulu.Command // Test that the --no-descriptions flag is present on all shells - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) for _, shell := range []string{"bash", "fish", "powershell", "zsh"} { compCmd, _, err = rootCmd.Find([]string{zulu.CompCmdName, shell}) - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") flag := compCmd.Flags().Lookup(zulu.CompCmdNoDescFlagName) - assertNotNilf(t, flag, "Missing --%s flag for %s shell", zulu.CompCmdNoDescFlagName, shell) + testutil.AssertNotNilf(t, flag, "Missing --%s flag for %s shell", zulu.CompCmdNoDescFlagName, shell) } // Remove completion command for the next test removeCompCmd(rootCmd) // Test that the '--no-descriptions' flag can be disabled rootCmd.CompletionOptions.DisableDescriptionsFlag = true - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) for _, shell := range []string{"fish", "zsh", "bash", "powershell"} { compCmd, _, err = rootCmd.Find([]string{zulu.CompCmdName, shell}) - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") flag := compCmd.Flags().Lookup(zulu.CompCmdNoDescFlagName) - assertNilf(t, flag, "Unexpected --%s flag for %s shell", zulu.CompCmdNoDescFlagName, shell) + testutil.AssertNilf(t, flag, "Unexpected --%s flag for %s shell", zulu.CompCmdNoDescFlagName, shell) } // Re-enable for next test rootCmd.CompletionOptions.DisableDescriptionsFlag = false @@ -1911,12 +2071,12 @@ func TestDefaultCompletionCmd(t *testing.T) { // Test that the '--no-descriptions' flag is disabled when descriptions are disabled rootCmd.CompletionOptions.DisableDescriptions = true - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) for _, shell := range []string{"fish", "zsh", "bash", "powershell"} { compCmd, _, err = rootCmd.Find([]string{zulu.CompCmdName, shell}) - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") flag := compCmd.Flags().Lookup(zulu.CompCmdNoDescFlagName) - assertNilf(t, flag, "Unexpected --%s flag for %s shell", zulu.CompCmdNoDescFlagName, shell) + testutil.AssertNilf(t, flag, "Unexpected --%s flag for %s shell", zulu.CompCmdNoDescFlagName, shell) } // Re-enable for next test rootCmd.CompletionOptions.DisableDescriptions = false @@ -1925,10 +2085,15 @@ func TestDefaultCompletionCmd(t *testing.T) { // Test that the 'completion' command can be hidden rootCmd.CompletionOptions.HiddenDefaultCmd = true - assertNil(t, rootCmd.Execute()) + testutil.AssertNil(t, rootCmd.Execute()) compCmd, _, err = rootCmd.Find([]string{zulu.CompCmdName}) - assertNilf(t, err, "Unexpected error: %v", err) - assertEqualf(t, true, compCmd.Hidden, "Default 'completion' command should be hidden but it is not") + testutil.AssertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertEqualf( + t, + true, + compCmd.Hidden, + "Default 'completion' command should be hidden but it is not", + ) // Re-enable for next test rootCmd.CompletionOptions.HiddenDefaultCmd = false // Remove completion command for the next test @@ -1945,7 +2110,7 @@ func TestCompleteCompletion(t *testing.T) { // Test sub-commands of the completion command output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "completion", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "bash", @@ -1955,7 +2120,7 @@ func TestCompleteCompletion(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test there are no completions for the sub-commands of the completion command var compCmd *zulu.Command @@ -1967,15 +2132,21 @@ func TestCompleteCompletion(t *testing.T) { } for _, shell := range compCmd.Commands() { - output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, zulu.CompCmdName, shell.Name(), "") - assertNilf(t, err, "Unexpected error: %v", err) + output, err = executeCommand( + rootCmd, + zulu.ShellCompNoDescRequestCmd, + zulu.CompCmdName, + shell.Name(), + "", + ) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", "", }, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } } @@ -1996,7 +2167,7 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { // Test that a single shorthand flag works output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-s", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "foo", @@ -2004,11 +2175,11 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that multiple boolean shorthand flags work output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-sd", "") - assertNilf(t, err, "Unexpected error %v", err) + testutil.AssertNilf(t, err, "Unexpected error %v", err) expected = strings.Join([]string{ "foo", @@ -2016,33 +2187,33 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that multiple boolean + string shorthand flags work output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-sdf", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "works", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that multiple boolean + string with equal sign shorthand flags work output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-sdf=") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "works", ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that multiple boolean + string with equal sign with value shorthand flags work output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "-sdf=abc", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "foo", @@ -2050,12 +2221,15 @@ func TestMultipleShorthandFlagCompletion(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestCompleteWithDisableFlagParsing(t *testing.T) { - - flagValidArgs := func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + flagValidArgs := func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return []string{"--flag", "-f"}, zulu.ShellCompDirectiveNoFileComp } @@ -2068,33 +2242,48 @@ func TestCompleteWithDisableFlagParsing(t *testing.T) { } rootCmd.AddCommand(childCmd) - rootCmd.PersistentFlags().String("persistent", "", "persistent flag", zflag.OptShorthand('p')) - childCmd.Flags().String("nonPersistent", "", "non-persistent flag", zflag.OptShorthand('n')) + rootCmd.PersistentFlags().String( + "persistent", + "", + "persistent flag", + zflag.OptShorthand('p'), + ) + childCmd.Flags().String( + "nonPersistent", + "", + "non-persistent flag", + zflag.OptShorthand('n'), + ) // Test that when DisableFlagParsing==true, ValidArgsFunction is called to complete flag names, // after Zulu tried to complete the flags it knows about. childCmd.DisableFlagParsing = true output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child", "-") - assertNilf(t, err, "Unexpected error: %v", err) - - expected := strings.Join([]string{ - "--persistent", - "-p", - "--help", - "-h", - "--nonPersistent", - "-n", - "--flag", - "-f", - ":4", - "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") + testutil.AssertNilf(t, err, "Unexpected error: %v", err) + + expected := strings.Join( + []string{ + "--persistent", + "-p", + "--help", + "-h", + "--nonPersistent", + "-n", + "--flag", + "-f", + ":4", + "Completion ended with directive: ShellCompDirectiveNoFileComp", + "", + }, + "\n", + ) - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Test that when DisableFlagParsing==false, Zulu completes the flags itself and ValidArgsFunction is not called childCmd.DisableFlagParsing = false output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child", "-") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) // Zulu was not told of any flags, so it returns nothing expected = strings.Join([]string{ @@ -2107,7 +2296,7 @@ func TestCompleteWithDisableFlagParsing(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestCompleteWithRootAndLegacyArgs(t *testing.T) { @@ -2124,7 +2313,7 @@ func TestCompleteWithRootAndLegacyArgs(t *testing.T) { // Make sure the first arg is completed output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "arg1", @@ -2132,11 +2321,11 @@ func TestCompleteWithRootAndLegacyArgs(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) // Make sure the completion of arguments continues output, err = executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "arg1", "") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected = strings.Join([]string{ "arg1", @@ -2144,7 +2333,7 @@ func TestCompleteWithRootAndLegacyArgs(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestFixedCompletions(t *testing.T) { @@ -2158,7 +2347,7 @@ func TestFixedCompletions(t *testing.T) { rootCmd.AddCommand(childCmd) output, err := executeCommand(rootCmd, zulu.ShellCompNoDescRequestCmd, "child", "a") - assertNilf(t, err, "Unexpected error: %v", err) + testutil.AssertNilf(t, err, "Unexpected error: %v", err) expected := strings.Join([]string{ "apple", @@ -2167,7 +2356,7 @@ func TestFixedCompletions(t *testing.T) { ":4", "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") - assertEqual(t, expected, output) + testutil.AssertEqual(t, expected, output) } func TestCompletionForGroupedFlags(t *testing.T) { @@ -2260,8 +2449,8 @@ func TestCompletionForGroupedFlags(t *testing.T) { args := []string{zulu.ShellCompNoDescRequestCmd} args = append(args, tc.args...) output, err := executeCommand(c, args...) - assertNilf(t, err, "Unexpected error %q", err) - assertEqual(t, tc.expectedOutput, output) + testutil.AssertNilf(t, err, "Unexpected error %q", err) + testutil.AssertEqual(t, tc.expectedOutput, output) }) } } @@ -2321,7 +2510,8 @@ func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), }, { - desc: "when flag in mutually exclusive group present, other flags in group not suggested even with the - prefix", + desc: "when flag in mutually exclusive group present," + + "other flags in group not suggested even with the - prefix", args: []string{"child", "--ingroup1", "8", "-"}, expectedOutput: strings.Join([]string{ "--ingroup1", // Should be suggested again since it is a slice @@ -2351,8 +2541,8 @@ func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { args = append(args, tc.args...) output, err := executeCommand(c, args...) - assertNilf(t, err, "Unexpected error %q", err) - assertEqual(t, tc.expectedOutput, output) + testutil.AssertNilf(t, err, "Unexpected error %q", err) + testutil.AssertEqual(t, tc.expectedOutput, output) }) } } @@ -2368,7 +2558,11 @@ func TestCompletionZuluFlags(t *testing.T) { Use: "child", Version: "1.1.1", RunE: noopRun, - ValidArgsFunction: func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + ValidArgsFunction: func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return []string{"extra"}, zulu.ShellCompDirectiveNoFileComp }, } @@ -2376,7 +2570,11 @@ func TestCompletionZuluFlags(t *testing.T) { Use: "child2", Version: "1.1.1", RunE: noopRun, - ValidArgsFunction: func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + ValidArgsFunction: func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return []string{"extra2"}, zulu.ShellCompDirectiveNoFileComp }, } @@ -2384,7 +2582,11 @@ func TestCompletionZuluFlags(t *testing.T) { Use: "child3", Version: "1.1.1", RunE: noopRun, - ValidArgsFunction: func(cmd *zulu.Command, args []string, toComplete string) ([]string, zulu.ShellCompDirective) { + ValidArgsFunction: func( + cmd *zulu.Command, + args []string, + toComplete string, + ) ([]string, zulu.ShellCompDirective) { return []string{"extra3"}, zulu.ShellCompDirectiveNoFileComp }, } @@ -2395,10 +2597,20 @@ func TestCompletionZuluFlags(t *testing.T) { // Have a command that adds its own help and version flag _ = childCmd2.Flags().Bool("help", false, "My own help", zflag.OptShorthand('h')) - _ = childCmd2.Flags().Bool("version", false, "My own version", zflag.OptShorthand('v')) + _ = childCmd2.Flags().Bool( + "version", + false, + "My own version", + zflag.OptShorthand('v'), + ) // Have a command that only adds its own -v flag - _ = childCmd3.Flags().Bool("verbose", false, "Not a version flag", zflag.OptShorthand('v')) + _ = childCmd3.Flags().Bool( + "verbose", + false, + "Not a version flag", + zflag.OptShorthand('v'), + ) return rootCmd } @@ -2539,8 +2751,8 @@ func TestCompletionZuluFlags(t *testing.T) { args = append(args, tc.args...) output, err := executeCommand(c, args...) - assertNilf(t, err, "Unexpected error %q", err) - assertEqual(t, tc.expectedOutput, output) + testutil.AssertNilf(t, err, "Unexpected error %q", err) + testutil.AssertEqual(t, tc.expectedOutput, output) }) } } @@ -2575,7 +2787,7 @@ func TestShellCompDirective_ListDirectives(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.d.ListDirectives() - assertEqual(t, tt.want, got) + testutil.AssertEqual(t, tt.want, got) }) } } diff --git a/doc/adoc_docs.go b/doc/adoc_docs.go index 8e24db8..4a1192c 100644 --- a/doc/adoc_docs.go +++ b/doc/adoc_docs.go @@ -41,6 +41,7 @@ func GenAsciidoc(cmd *zulu.Command, w io.Writer) error { func GenAsciidocCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) string) error { cmd.InitDefaultHelpCmd() cmd.InitDefaultHelpFlag() + cmd.InitDefaultCompletionCmd() buf := new(bytes.Buffer) name := cmd.CommandPath() @@ -108,7 +109,7 @@ func GenAsciidocCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) // help output will be in the file `cmd-sub-third.1`. func GenAsciidocTree(cmd *zulu.Command, dir string) error { identity := func(s string) string { return s } - emptyStr := func(s string) string { return "" } + emptyStr := func(_ string) string { return "" } return GenAsciidocTreeCustom(cmd, dir, emptyStr, identity) } @@ -135,8 +136,6 @@ func GenAsciidocTreeCustom(cmd *zulu.Command, dir string, filePrepender, linkHan if _, err := io.WriteString(f, filePrepender(filename)); err != nil { return err } - if err := GenAsciidocCustom(cmd, f, linkHandler); err != nil { - return err - } - return nil + + return GenAsciidocCustom(cmd, f, linkHandler) } diff --git a/doc/adoc_docs_test.go b/doc/adoc_docs_test.go index ed31dbf..7bfd5ca 100644 --- a/doc/adoc_docs_test.go +++ b/doc/adoc_docs_test.go @@ -8,9 +8,12 @@ import ( "github.com/zulucmd/zulu/v2" "github.com/zulucmd/zulu/v2/doc" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestGenAsciidoc(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() + // We generate on subcommand so we have both subcommands and parents. buf := new(bytes.Buffer) if err := doc.GenAsciidoc(echoCmd, buf); err != nil { @@ -18,17 +21,18 @@ func TestGenAsciidoc(t *testing.T) { } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertNotContains(t, output, deprecatedCmd.Short) - assertContains(t, output, "Options inherited from parent commands") + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertNotContains(t, output, deprecatedCmd.Short) + testutil.AssertContains(t, output, "Options inherited from parent commands") } func TestGenAsciidocWithNoLongOrSynopsis(t *testing.T) { + _, _, _, _, _, _, dummyCmd := getTestCmds() // We generate on subcommand so we have both subcommands and parents. buf := new(bytes.Buffer) if err := doc.GenAsciidoc(dummyCmd, buf); err != nil { @@ -36,18 +40,18 @@ func TestGenAsciidocWithNoLongOrSynopsis(t *testing.T) { } output := buf.String() - assertContains(t, output, dummyCmd.Example) - assertContains(t, output, dummyCmd.Short) - assertContains(t, output, "Options inherited from parent commands") - assertNotContains(t, output, "### Synopsis") + testutil.AssertContains(t, output, dummyCmd.Example) + testutil.AssertContains(t, output, dummyCmd.Short) + testutil.AssertContains(t, output, "Options inherited from parent commands") + testutil.AssertNotContains(t, output, "### Synopsis") } func TestGenAsciidocNoHiddenParents(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() // We generate on subcommand so we have both subcommands and parents. for _, name := range []string{"rootflag", "strtwo"} { f := rootCmd.PersistentFlags().Lookup(name) f.Hidden = true - defer func() { f.Hidden = false }() } buf := new(bytes.Buffer) if err := doc.GenAsciidoc(echoCmd, buf); err != nil { @@ -55,19 +59,20 @@ func TestGenAsciidocNoHiddenParents(t *testing.T) { } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertNotContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertNotContains(t, output, deprecatedCmd.Short) - assertNotContains(t, output, "Options inherited from parent commands") + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertNotContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertNotContains(t, output, deprecatedCmd.Short) + testutil.AssertNotContains(t, output, "Options inherited from parent commands") } func TestGenAsciidocNoTag(t *testing.T) { + rootCmd, _, _, _, _, _, _ := getTestCmds() + rootCmd.DisableAutoGenTag = true - defer func() { rootCmd.DisableAutoGenTag = false }() buf := new(bytes.Buffer) if err := doc.GenAsciidoc(rootCmd, buf); err != nil { @@ -75,16 +80,13 @@ func TestGenAsciidocNoTag(t *testing.T) { } output := buf.String() - assertNotContains(t, output, "Auto generated") + testutil.AssertNotContains(t, output, "Auto generated") } func TestGenAsciidocTree(t *testing.T) { c := &zulu.Command{Use: "do [OPTIONS] arg1 arg2"} - tmpdir, err := os.MkdirTemp("", "test-gen-md-tree") - if err != nil { - t.Fatalf("Failed to create tmpdir: %v", err) - } - defer os.RemoveAll(tmpdir) + + tmpdir := t.TempDir() if err := doc.GenAsciidocTree(c, tmpdir); err != nil { t.Fatalf("GenAsciidocTree failed: %v", err) @@ -96,15 +98,15 @@ func TestGenAsciidocTree(t *testing.T) { } func BenchmarkGenAsciidocToFile(b *testing.B) { - file, err := os.CreateTemp("", "") + rootCmd, _, _, _, _, _, _ := getTestCmds() + file, err := os.CreateTemp(b.TempDir(), "") if err != nil { b.Fatal(err) } - defer os.Remove(file.Name()) defer file.Close() b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { if err := doc.GenAsciidoc(rootCmd, file); err != nil { b.Fatal(err) } diff --git a/doc/cmd_test.go b/doc/cmd_test.go index d4d147b..c2744dc 100644 --- a/doc/cmd_test.go +++ b/doc/cmd_test.go @@ -1,112 +1,143 @@ +//nolint:gochecknoglobals // these are tests, we don't care package doc_test import ( - "regexp" - "strings" - "testing" - "github.com/zulucmd/zflag/v2" "github.com/zulucmd/zulu/v2" ) func emptyRun(*zulu.Command, []string) error { return nil } -func init() { - rootCmd.PersistentFlags().String("rootflag", "two", "", zflag.OptShorthand('r')) - rootCmd.PersistentFlags().String("strtwo", "two", "help message for parent flag strtwo", zflag.OptShorthand('t')) - - echoCmd.PersistentFlags().String("strone", "one", "help message for flag strone", zflag.OptShorthand('s')) - echoCmd.PersistentFlags().Bool("persistentbool", false, "help message for flag persistentbool", zflag.OptShorthand('p')) - echoCmd.Flags().Int("intone", 123, "help message for flag intone", zflag.OptShorthand('i')) - echoCmd.Flags().Bool("boolone", true, "help message for flag boolone", zflag.OptShorthand('b')) - - timesCmd.PersistentFlags().String("strtwo", "2", "help message for child flag strtwo", zflag.OptShorthand('t')) - timesCmd.Flags().Int("inttwo", 234, "help message for flag inttwo", zflag.OptShorthand('j')) - timesCmd.Flags().Bool("booltwo", false, "help message for flag booltwo", zflag.OptShorthand('c')) - - printCmd.PersistentFlags().String("strthree", "three", "help message for flag strthree", zflag.OptShorthand('s')) - printCmd.Flags().Int("intthree", 345, "help message for flag intthree", zflag.OptShorthand('i')) - printCmd.Flags().Bool("boolthree", true, "help message for flag boolthree", zflag.OptShorthand('b')) - - echoCmd.AddCommand(timesCmd, echoSubCmd, deprecatedCmd) - rootCmd.AddCommand(printCmd, echoCmd, dummyCmd) -} - -var rootCmd = &zulu.Command{ - Use: "root", - Short: "Root short description", - Long: "Root long description", - RunE: emptyRun, -} - -var echoCmd = &zulu.Command{ - Use: "echo [string to echo]", - Aliases: []string{"say"}, - Short: "Echo anything to the screen", - Long: "an utterly useless command for testing", - Example: "Just run zulu-test echo", -} - -var echoSubCmd = &zulu.Command{ - Use: "echosub [string to print]", - Short: "second sub command for echo", - Long: "an absolutely utterly useless command for testing gendocs!.", - RunE: emptyRun, -} +func getTestCmds() ( + *zulu.Command, + *zulu.Command, + *zulu.Command, + *zulu.Command, + *zulu.Command, + *zulu.Command, + *zulu.Command, +) { + var rootCmd = &zulu.Command{ + Use: "root", + Short: "Root short description", + Long: "Root long description", + RunE: emptyRun, + } -var timesCmd = &zulu.Command{ - Use: "times [# times] [string to echo]", - SuggestFor: []string{"counts"}, - Short: "Echo anything to the screen more times", - Long: `a slightly useless command for testing.`, - RunE: emptyRun, -} + var echoCmd = &zulu.Command{ + Use: "echo [string to echo]", + Aliases: []string{"say"}, + Short: "Echo anything to the screen", + Long: "an utterly useless command for testing", + Example: "Just run zulu-test echo", + } -var deprecatedCmd = &zulu.Command{ - Use: "deprecated [can't do anything here]", - Short: "A command which is deprecated", - Long: `an absolutely utterly useless command for testing deprecation!.`, - Deprecated: "Please use echo instead", -} + var echoSubCmd = &zulu.Command{ + Use: "echosub [string to print]", + Short: "second sub command for echo", + Long: "an absolutely utterly useless command for testing gendocs!.", + RunE: emptyRun, + } -var printCmd = &zulu.Command{ - Use: "print [string to print]", - Short: "Print anything to the screen", - Long: `an absolutely utterly useless command for testing.`, -} + var timesCmd = &zulu.Command{ + Use: "times [# times] [string to echo]", + SuggestFor: []string{"counts"}, + Short: "Echo anything to the screen more times", + Long: `a slightly useless command for testing.`, + RunE: emptyRun, + } -var dummyCmd = &zulu.Command{ - Use: "dummy [action]", - Short: "Performs a dummy action", -} + var deprecatedCmd = &zulu.Command{ + Use: "deprecated [can't do anything here]", + Short: "A command which is deprecated", + Long: `an absolutely utterly useless command for testing deprecation!.`, + Deprecated: "Please use echo instead", + } -func assertNotContains(t *testing.T, str, unexpected string) { - t.Helper() - assertNotContainsf(t, str, unexpected, "%q should not contain %q", str, unexpected) -} + var printCmd = &zulu.Command{ + Use: "print [string to print]", + Short: "Print anything to the screen", + Long: `an absolutely utterly useless command for testing.`, + } -func assertNotContainsf(t *testing.T, str, unexpected string, msg string, fmt ...interface{}) { - t.Helper() - if strings.Contains(str, unexpected) { - t.Errorf(msg, fmt...) + var dummyCmd = &zulu.Command{ + Use: "dummy [action]", + Short: "Performs a dummy action", } -} -func assertContains(t *testing.T, str, substr string) { - t.Helper() - assertContainsf(t, str, substr, "%q does not contain %q", str, substr) -} + rootCmd.PersistentFlags().String("rootflag", "two", "", zflag.OptShorthand('r')) + rootCmd.PersistentFlags().String( + "strtwo", + "two", + "help message for parent flag strtwo", + zflag.OptShorthand('t'), + ) + + echoCmd.PersistentFlags().String( + "strone", + "one", + "help message for flag strone", + zflag.OptShorthand('s'), + ) + echoCmd.PersistentFlags().Bool( + "persistentbool", + false, + "help message for flag persistentbool", + zflag.OptShorthand('p'), + ) + echoCmd.Flags().Int( + "intone", + 123, + "help message for flag intone", + zflag.OptShorthand('i'), + ) + echoCmd.Flags().Bool( + "boolone", + true, + "help message for flag boolone", + zflag.OptShorthand('b'), + ) + + timesCmd.PersistentFlags().String( + "strtwo", + "2", + "help message for child flag strtwo", + zflag.OptShorthand('t'), + ) + timesCmd.Flags().Int( + "inttwo", + 234, + "help message for flag inttwo", + zflag.OptShorthand('j'), + ) + timesCmd.Flags().Bool( + "booltwo", + false, + "help message for flag booltwo", + zflag.OptShorthand('c'), + ) + + printCmd.PersistentFlags().String( + "strthree", + "three", + "help message for flag strthree", + zflag.OptShorthand('s'), + ) + printCmd.Flags().Int( + "intthree", + 345, + "help message for flag intthree", + zflag.OptShorthand('i'), + ) + printCmd.Flags().Bool( + "boolthree", + true, + "help message for flag boolthree", + zflag.OptShorthand('b'), + ) -func assertContainsf(t *testing.T, str, expected string, msg string, fmt ...interface{}) { - t.Helper() - if !strings.Contains(str, expected) { - t.Errorf(msg, fmt...) - } -} + echoCmd.AddCommand(timesCmd, echoSubCmd, deprecatedCmd) + rootCmd.AddCommand(printCmd, echoCmd, dummyCmd) -func assertMatch(t *testing.T, str, pattern string) { - t.Helper() - if ok, _ := regexp.MatchString(pattern, str); !ok { - t.Errorf("Expected to match: \n%v\nGot:\n %v\n", pattern, str) - } + return rootCmd, echoCmd, echoSubCmd, timesCmd, deprecatedCmd, printCmd, dummyCmd } diff --git a/doc/exports_test.go b/doc/exports_test.go index 615a497..50ef18b 100644 --- a/doc/exports_test.go +++ b/doc/exports_test.go @@ -1,3 +1,4 @@ +//nolint:testpackage // this is explicitly design to expose some functions in tests package doc var ManPrintFlags = manPrintFlags diff --git a/doc/man_docs.go b/doc/man_docs.go index 1ee90ed..d16efb7 100644 --- a/doc/man_docs.go +++ b/doc/man_docs.go @@ -24,11 +24,10 @@ import ( "strings" "time" - "github.com/zulucmd/zulu/v2" - "github.com/zulucmd/zulu/v2/internal/util" - "github.com/cpuguy83/go-md2man/v2/md2man" "github.com/zulucmd/zflag/v2" + "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/util" ) // GenManTree will generate a man page for this command and all descendants @@ -95,8 +94,7 @@ type GenManTreeOptions struct { type GenManHeader struct { Title string Section string - Date *time.Time - date string + Date time.Time Source string Manual string } @@ -131,18 +129,17 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error { if header.Section == "" { header.Section = "1" } - if header.Date == nil { - now := time.Now() + if header.Date.IsZero() { if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" { unixEpoch, err := strconv.ParseInt(epoch, 10, 64) if err != nil { - return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err) + return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %w", err) } - now = time.Unix(unixEpoch, 0) + header.Date = time.Unix(unixEpoch, 0) + } else { + header.Date = time.Now() } - header.Date = &now } - header.date = (*header.Date).Format("Jan 2006") if header.Source == "" && !disableAutoGen { header.Source = "Auto generated by zulucmd/zulu" } @@ -155,9 +152,10 @@ func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *zulu.Command, d description = cmd.Short } + date := header.Date.Format("Jan 2006") util.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s" # NAME -`, header.Title, header.Section, header.date, header.Source, header.Manual)) +`, header.Title, header.Section, date, header.Source, header.Manual)) util.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short)) util.WriteStringAndCheck(buf, "# SYNOPSIS\n") util.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine())) @@ -176,7 +174,7 @@ func manPrintCommands(buf io.StringWriter, header *GenManHeader, cmd *zulu.Comma } // No need to go further if there is no sub-commands to document - if len(subCommands) <= 0 { + if len(subCommands) == 0 { return } @@ -190,10 +188,14 @@ func manPrintCommands(buf io.StringWriter, header *GenManHeader, cmd *zulu.Comma if len(c.Short) > 0 { short = fmt.Sprintf(" %s\n", c.Short) } - util.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n%s See **%s(%s)**.\n\n", c.Name(), short, dashedPath, header.Section)) + util.WriteStringAndCheck( + buf, + fmt.Sprintf("**%s**\n\n%s See **%s(%s)**.\n\n", c.Name(), short, dashedPath, header.Section), + ) } } +//nolint:gocognit // todo later func manPrintFlags(buf io.StringWriter, flags *zflag.FlagSet) { flags.VisitAll(func(flag *zflag.Flag) { if len(flag.Deprecated) > 0 || flag.Hidden { @@ -211,7 +213,7 @@ func manPrintFlags(buf io.StringWriter, flags *zflag.FlagSet) { var args []any hasShorthand := flag.Shorthand > 0 && len(flag.ShorthandDeprecated) == 0 - if hasShorthand { + if hasShorthand { //nolint:nestif // todo refactor later format += "**-%c**" args = append(args, flag.Shorthand) @@ -232,7 +234,7 @@ func manPrintFlags(buf io.StringWriter, flags *zflag.FlagSet) { } } - if !hasShorthand || !flag.ShorthandOnly { + if !hasShorthand || !flag.ShorthandOnly { //nolint:nestif // todo refactor later if isBoolean { if flag.AddNegative { format += ", **--[no-]%s**" diff --git a/doc/man_docs_test.go b/doc/man_docs_test.go index 5ce9801..8cd514c 100644 --- a/doc/man_docs_test.go +++ b/doc/man_docs_test.go @@ -10,9 +10,9 @@ import ( "testing" "github.com/zulucmd/zflag/v2" - "github.com/zulucmd/zulu/v2" "github.com/zulucmd/zulu/v2/doc" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func translate(in string) string { @@ -20,6 +20,7 @@ func translate(in string) string { } func TestGenManDoc(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() header := &doc.GenManHeader{ Title: "Project", Section: "2", @@ -37,19 +38,20 @@ func TestGenManDoc(t *testing.T) { dashParentPath := strings.ReplaceAll(parentPath, " ", "-") expected := translate(dashParentPath) expected = expected + "(" + header.Section + ")" - assertContains(t, output, expected) - - assertContains(t, output, translate(echoCmd.Name())) - assertContains(t, output, translate(echoCmd.Name())) - assertContains(t, output, "boolone") - assertContains(t, output, "rootflag") - assertContains(t, output, translate(rootCmd.Name())) - assertContains(t, output, translate(echoSubCmd.Name())) - assertNotContains(t, output, translate(deprecatedCmd.Name())) - assertContains(t, output, translate("Auto generated")) + testutil.AssertContains(t, output, expected) + + testutil.AssertContains(t, output, translate(echoCmd.Name())) + testutil.AssertContains(t, output, translate(echoCmd.Name())) + testutil.AssertContains(t, output, "boolone") + testutil.AssertContains(t, output, "rootflag") + testutil.AssertContains(t, output, translate(rootCmd.Name())) + testutil.AssertContains(t, output, translate(echoSubCmd.Name())) + testutil.AssertNotContains(t, output, translate(deprecatedCmd.Name())) + testutil.AssertContains(t, output, translate("Auto generated")) } func TestGenManNoHiddenParents(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() header := &doc.GenManHeader{ Title: "Project", Section: "2", @@ -59,7 +61,6 @@ func TestGenManNoHiddenParents(t *testing.T) { for _, name := range []string{"rootflag", "strtwo"} { f := rootCmd.PersistentFlags().Lookup(name) f.Hidden = true - defer func() { f.Hidden = false }() } buf := new(bytes.Buffer) if err := doc.GenMan(echoCmd, header, buf); err != nil { @@ -72,22 +73,22 @@ func TestGenManNoHiddenParents(t *testing.T) { dashParentPath := strings.ReplaceAll(parentPath, " ", "-") expected := translate(dashParentPath) expected = expected + "(" + header.Section + ")" - assertContains(t, output, expected) - - assertContains(t, output, translate(echoCmd.Name())) - assertContains(t, output, translate(echoCmd.Name())) - assertContains(t, output, "boolone") - assertNotContains(t, output, "rootflag") - assertContains(t, output, translate(rootCmd.Name())) - assertContains(t, output, translate(echoSubCmd.Name())) - assertNotContains(t, output, translate(deprecatedCmd.Name())) - assertContains(t, output, translate("Auto generated")) - assertNotContains(t, output, "OPTIONS INHERITED FROM PARENT COMMANDS") + testutil.AssertContains(t, output, expected) + + testutil.AssertContains(t, output, translate(echoCmd.Name())) + testutil.AssertContains(t, output, translate(echoCmd.Name())) + testutil.AssertContains(t, output, "boolone") + testutil.AssertNotContains(t, output, "rootflag") + testutil.AssertContains(t, output, translate(rootCmd.Name())) + testutil.AssertContains(t, output, translate(echoSubCmd.Name())) + testutil.AssertNotContains(t, output, translate(deprecatedCmd.Name())) + testutil.AssertContains(t, output, translate("Auto generated")) + testutil.AssertNotContains(t, output, "OPTIONS INHERITED FROM PARENT COMMANDS") } func TestGenManNoGenTag(t *testing.T) { + _, echoCmd, _, _, _, _, _ := getTestCmds() echoCmd.DisableAutoGenTag = true - defer func() { echoCmd.DisableAutoGenTag = false }() header := &doc.GenManHeader{ Title: "Project", @@ -102,18 +103,15 @@ func TestGenManNoGenTag(t *testing.T) { output := buf.String() unexpected := translate("#HISTORY") - assertNotContains(t, output, unexpected) + testutil.AssertNotContains(t, output, unexpected) unexpected = translate("Auto generated by zulucmd/zulu") - assertNotContains(t, output, unexpected) + testutil.AssertNotContains(t, output, unexpected) } func TestGenManNoGenTagWithDisabledParent(t *testing.T) { + rootCmd, echoCmd, _, _, _, _, _ := getTestCmds() // We set the flag on a parent to check it is used in its descendance rootCmd.DisableAutoGenTag = true - defer func() { - echoCmd.DisableAutoGenTag = false - rootCmd.DisableAutoGenTag = false - }() header := &doc.GenManHeader{ Title: "Project", @@ -128,20 +126,34 @@ func TestGenManNoGenTagWithDisabledParent(t *testing.T) { output := buf.String() unexpected := translate("#HISTORY") - assertNotContains(t, output, unexpected) + testutil.AssertNotContains(t, output, unexpected) unexpected = translate("Auto generated by zulucmd/zulu") - assertNotContains(t, output, unexpected) + testutil.AssertNotContains(t, output, unexpected) } func TestGenManSeeAlso(t *testing.T) { - rootCmd := &zulu.Command{Use: "git", Short: "the stupid content tracker", Long: "Git is a fast, scalable, distributed revision control system with an unusually rich command set that provides both high-level operations and full access to internals.", RunE: emptyRun} + rootCmd := &zulu.Command{ + Use: "git", + Short: "the stupid content tracker", + Long: "Git is a fast, scalable, distributed revision control system with an unusually rich command " + + "set that provides both high-level operations and full access to internals.", + RunE: emptyRun, + } aCmd := &zulu.Command{Use: "clone", RunE: emptyRun, Hidden: true} // #229 bCmd := &zulu.Command{Use: "checkout", RunE: emptyRun} cCmd := &zulu.Command{Use: "branch", RunE: emptyRun} rootCmd.AddCommand(aCmd, bCmd, cCmd) - // todo add the flags in the SYNOPSIS section. Instead of just writing "git [flags]", it should write "git [-C ]" - rootCmd.Flags().String("chdir", "", "Run as if git was started in instead of the current working directory.", zflag.OptShorthand('C'), zflag.OptShorthandOnly(), zflag.OptUsageType("")) + // todo add the flags in the SYNOPSIS section. Instead of just writing "git [flags]", + // it should write "git [-C ]" + rootCmd.Flags().String( + "chdir", + "", + "Run as if git was started in instead of the current working directory.", + zflag.OptShorthand('C'), + zflag.OptShorthandOnly(), + zflag.OptUsageType(""), + ) buf := new(bytes.Buffer) header := &doc.GenManHeader{} @@ -153,14 +165,23 @@ func TestGenManSeeAlso(t *testing.T) { if err := assertLineFound(scanner, ".SH SEE ALSO"); err != nil { t.Fatalf("Couldn't find SEE ALSO section header: %v", err) } - if err := assertNextLineEquals(scanner, `\fBgit-branch(1)\fP, \fBgit-checkout(1)\fP, \fBgit-completion(1)\fP`); err != nil { + if err := assertNextLineEquals( + scanner, + `\fBgit-branch(1)\fP, \fBgit-checkout(1)\fP, \fBgit-completion(1)\fP`, + ); err != nil { t.Fatalf("Second line after SEE ALSO wasn't correct: %v", err) } } func TestManPrintFlagsHidesShortDeprecated(t *testing.T) { c := &zulu.Command{} - c.Flags().String("foo", "default", "Foo flag", zflag.OptShorthand('f'), zflag.OptShorthandDeprecated("don't use it no more")) + c.Flags().String( + "foo", + "default", + "Foo flag", + zflag.OptShorthand('f'), + zflag.OptShorthandDeprecated("don't use it no more"), + ) buf := new(bytes.Buffer) doc.ManPrintFlags(buf, c.Flags()) @@ -173,6 +194,7 @@ func TestManPrintFlagsHidesShortDeprecated(t *testing.T) { } func TestGenManCommands(t *testing.T) { + rootCmd, echoCmd, _, timesCmd, _, _, _ := getTestCmds() header := &doc.GenManHeader{ Title: "Project", Section: "2", @@ -185,14 +207,14 @@ func TestGenManCommands(t *testing.T) { } output := buf.String() - assertContains(t, output, ".SH COMMANDS") - assertMatch(t, output, `\\fBecho\\fP + testutil.AssertContains(t, output, ".SH COMMANDS") + testutil.AssertMatch(t, output, `\\fBecho\\fP \.EX Echo anything to the screen See \*\*root\-echo\(2\)\*\*\. \.EE`) - assertNotContains(t, output, ".PP\n\\fBprint\\fP\n") + testutil.AssertNotContains(t, output, ".PP\n\\fBprint\\fP\n") // Echo command buf = new(bytes.Buffer) @@ -201,20 +223,20 @@ See \*\*root\-echo\(2\)\*\*\. } output = buf.String() - assertContains(t, output, ".SH COMMANDS") - assertMatch(t, output, `\\fBtimes\\fP + testutil.AssertContains(t, output, ".SH COMMANDS") + testutil.AssertMatch(t, output, `\\fBtimes\\fP \.EX Echo anything to the screen more times See \*\*root\-echo\-times\(2\)\*\*\. \.EE`) - assertMatch(t, output, `\\fBechosub\\fP + testutil.AssertMatch(t, output, `\\fBechosub\\fP \.EX second sub command for echo See \*\*root\-echo\-echosub\(2\)\*\*\. \.EE`) - assertNotContains(t, output, ".PP\n\\fBdeprecated\\fP\n") + testutil.AssertNotContains(t, output, ".PP\n\\fBdeprecated\\fP\n") // Time command as echo's subcommand buf = new(bytes.Buffer) @@ -223,17 +245,13 @@ See \*\*root\-echo\-echosub\(2\)\*\*\. } output = buf.String() - assertNotContains(t, output, ".SH COMMANDS") + testutil.AssertNotContains(t, output, ".SH COMMANDS") } func TestGenManTree(t *testing.T) { c := &zulu.Command{Use: "do [OPTIONS] arg1 arg2"} header := &doc.GenManHeader{Section: "2"} - tmpdir, err := os.MkdirTemp("", "test-gen-man-tree") - if err != nil { - t.Fatalf("Failed to create tmpdir: %s", err.Error()) - } - defer os.RemoveAll(tmpdir) + tmpdir := t.TempDir() if err := doc.GenManTree(c, header, tmpdir); err != nil { t.Fatalf("GenManTree failed: %s", err.Error()) @@ -257,7 +275,7 @@ func assertLineFound(scanner *bufio.Scanner, expectedLine string) error { } if err := scanner.Err(); err != nil { - return fmt.Errorf("scan failed: %s", err) + return fmt.Errorf("scan failed: %w", err) } return fmt.Errorf("hit EOF before finding %v", expectedLine) @@ -273,14 +291,15 @@ func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error { } if err := scanner.Err(); err != nil { - return fmt.Errorf("scan failed: %v", err) + return fmt.Errorf("scan failed: %w", err) } return fmt.Errorf("hit EOF before finding %v", expectedLine) } func BenchmarkGenManToFile(b *testing.B) { - file, err := os.CreateTemp("", "") + rootCmd, _, _, _, _, _, _ := getTestCmds() + file, err := os.CreateTemp(b.TempDir(), "") if err != nil { b.Fatal(err) } @@ -288,7 +307,7 @@ func BenchmarkGenManToFile(b *testing.B) { defer file.Close() b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { if err := doc.GenMan(rootCmd, nil, file); err != nil { b.Fatal(err) } diff --git a/doc/man_examples_test.go b/doc/man_examples_test.go index 3488f6e..96d4992 100644 --- a/doc/man_examples_test.go +++ b/doc/man_examples_test.go @@ -3,12 +3,32 @@ package doc_test import ( "bytes" "fmt" + "strings" + "time" "github.com/zulucmd/zulu/v2" "github.com/zulucmd/zulu/v2/doc" "github.com/zulucmd/zulu/v2/internal/util" ) +// this is needed due to https://github.com/golang/go/issues/59191 +func collapseLines(text string) string { + lines := strings.Split(text, "\n") + n := 0 + for _, line := range lines { + if line != "" || n > 0 && lines[n-1] != "" { + lines[n] = line + n++ + } + } + + lines = lines[0:n] + if n > 0 && lines[n-1] != "" { + lines = append(lines, "") + } + return strings.Join(lines, "\n") +} + func ExampleGenManTree() { cmd := &zulu.Command{ Use: "test", @@ -19,6 +39,8 @@ func ExampleGenManTree() { Section: "3", } util.CheckErr(doc.GenManTree(cmd, header, "/tmp")) + + // Output: } func ExampleGenMan() { @@ -29,8 +51,33 @@ func ExampleGenMan() { header := &doc.GenManHeader{ Title: "MINE", Section: "3", + Date: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), } out := new(bytes.Buffer) - util.CheckErr(doc.GenMan(cmd, header, out)) - fmt.Print(out.String()) + err := doc.GenMan(cmd, header, out) + util.CheckErr(err) + fmt.Print(collapseLines(out.String())) + + // Output: + // .nh + // .TH "MINE" "3" "Jan 2020" "Auto generated by zulucmd/zulu" "" + // + // .SH NAME + // test - my test program + // + // .SH SYNOPSIS + // \fBtest [flags]\fP + // + // .SH DESCRIPTION + // my test program + // + // .SH OPTIONS + // \fB-h\fP, \fB--help\fP + // + // .EX + // help for test + // .EE + // + // .SH HISTORY + // 1-Jan-2020 Auto generated by zulucmd/zulu } diff --git a/doc/md_docs.go b/doc/md_docs.go index 04aff6c..ec68da9 100644 --- a/doc/md_docs.go +++ b/doc/md_docs.go @@ -26,7 +26,7 @@ import ( "github.com/zulucmd/zulu/v2" ) -func printOptions(buf *bytes.Buffer, cmd *zulu.Command, name string) { +func printOptions(buf *bytes.Buffer, cmd *zulu.Command) { flags := cmd.NonInheritedFlags() flags.SetOutput(buf) if flags.HasAvailableFlags() { @@ -74,37 +74,9 @@ func GenMarkdownCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example)) } - printOptions(buf, cmd, name) - - if hasSeeAlso(cmd) { - buf.WriteString("### SEE ALSO\n\n") - if cmd.HasParent() { - parent := cmd.Parent() - pname := parent.CommandPath() - link := pname + ".md" - link = strings.ReplaceAll(link, " ", "_") - buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short)) - cmd.VisitParents(func(c *zulu.Command) { - if c.DisableAutoGenTag { - cmd.DisableAutoGenTag = c.DisableAutoGenTag - } - }) - } - - children := cmd.Commands() - sort.Sort(byName(children)) + printOptions(buf, cmd) + printSeeAlsoMarkdown(cmd, buf, linkHandler, name) - for _, child := range children { - if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() { - continue - } - cname := name + " " + child.Name() - link := cname + ".md" - link = strings.ReplaceAll(link, " ", "_") - buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short)) - } - buf.WriteString("\n") - } if !cmd.DisableAutoGenTag { buf.WriteString("###### Auto generated by zulucmd/zulu on " + time.Now().Format("2-Jan-2006") + "\n") } @@ -112,6 +84,40 @@ func GenMarkdownCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) return err } +func printSeeAlsoMarkdown(cmd *zulu.Command, buf *bytes.Buffer, linkHandler func(string) string, name string) { + if !hasSeeAlso(cmd) { + return + } + + buf.WriteString("### SEE ALSO\n\n") + if cmd.HasParent() { + parent := cmd.Parent() + pname := parent.CommandPath() + link := pname + ".md" + link = strings.ReplaceAll(link, " ", "_") + buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short)) + cmd.VisitParents(func(c *zulu.Command) { + if c.DisableAutoGenTag { + cmd.DisableAutoGenTag = c.DisableAutoGenTag + } + }) + } + + children := cmd.Commands() + sort.Sort(byName(children)) + + for _, child := range children { + if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() { + continue + } + cname := name + " " + child.Name() + link := cname + ".md" + link = strings.ReplaceAll(link, " ", "_") + buf.WriteString(fmt.Sprintf("* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short)) + } + buf.WriteString("\n") +} + // GenMarkdownTree will generate a markdown page for this command and all // descendants in the directory given. The header may be nil. // This function may not work correctly if your command names have `-` in them. @@ -120,7 +126,7 @@ func GenMarkdownCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) // help output will be in the file `cmd-sub-third.1`. func GenMarkdownTree(cmd *zulu.Command, dir string) error { identity := func(s string) string { return s } - emptyStr := func(s string) string { return "" } + emptyStr := func(_ string) string { return "" } return GenMarkdownTreeCustom(cmd, dir, emptyStr, identity) } @@ -147,8 +153,6 @@ func GenMarkdownTreeCustom(cmd *zulu.Command, dir string, filePrepender, linkHan if _, err := io.WriteString(f, filePrepender(filename)); err != nil { return err } - if err := GenMarkdownCustom(cmd, f, linkHandler); err != nil { - return err - } - return nil + + return GenMarkdownCustom(cmd, f, linkHandler) } diff --git a/doc/md_docs_test.go b/doc/md_docs_test.go index c8e208b..21702a9 100644 --- a/doc/md_docs_test.go +++ b/doc/md_docs_test.go @@ -8,43 +8,47 @@ import ( "github.com/zulucmd/zulu/v2" "github.com/zulucmd/zulu/v2/doc" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestGenMdDoc(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() buf := new(bytes.Buffer) if err := doc.GenMarkdown(echoCmd, buf); err != nil { t.Fatal(err) } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertNotContains(t, output, deprecatedCmd.Short) - assertContains(t, output, "Options inherited from parent commands") + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertNotContains(t, output, deprecatedCmd.Short) + testutil.AssertContains(t, output, "Options inherited from parent commands") } func TestGenMdDocWithNoLongOrSynopsis(t *testing.T) { + _, _, _, _, _, _, dummyCmd := getTestCmds() + buf := new(bytes.Buffer) if err := doc.GenMarkdown(dummyCmd, buf); err != nil { t.Fatal(err) } output := buf.String() - assertContains(t, output, dummyCmd.Example) - assertContains(t, output, dummyCmd.Short) - assertContains(t, output, "Options inherited from parent commands") - assertNotContains(t, output, "### Synopsis") + testutil.AssertContains(t, output, dummyCmd.Example) + testutil.AssertContains(t, output, dummyCmd.Short) + testutil.AssertContains(t, output, "Options inherited from parent commands") + testutil.AssertNotContains(t, output, "### Synopsis") } func TestGenMdNoHiddenParents(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() for _, name := range []string{"rootflag", "strtwo"} { f := rootCmd.PersistentFlags().Lookup(name) f.Hidden = true - defer func() { f.Hidden = false }() } buf := new(bytes.Buffer) if err := doc.GenMarkdown(echoCmd, buf); err != nil { @@ -52,19 +56,19 @@ func TestGenMdNoHiddenParents(t *testing.T) { } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertNotContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertNotContains(t, output, deprecatedCmd.Short) - assertNotContains(t, output, "Options inherited from parent commands") + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertNotContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertNotContains(t, output, deprecatedCmd.Short) + testutil.AssertNotContains(t, output, "Options inherited from parent commands") } func TestGenMdNoTag(t *testing.T) { + rootCmd, _, _, _, _, _, _ := getTestCmds() rootCmd.DisableAutoGenTag = true - defer func() { rootCmd.DisableAutoGenTag = false }() buf := new(bytes.Buffer) if err := doc.GenMarkdown(rootCmd, buf); err != nil { @@ -72,16 +76,12 @@ func TestGenMdNoTag(t *testing.T) { } output := buf.String() - assertNotContains(t, output, "Auto generated") + testutil.AssertNotContains(t, output, "Auto generated") } func TestGenMdTree(t *testing.T) { c := &zulu.Command{Use: "do [OPTIONS] arg1 arg2"} - tmpdir, err := os.MkdirTemp("", "test-gen-md-tree") - if err != nil { - t.Fatalf("Failed to create tmpdir: %v", err) - } - defer os.RemoveAll(tmpdir) + tmpdir := t.TempDir() if err := doc.GenMarkdownTree(c, tmpdir); err != nil { t.Fatalf("GenMarkdownTree failed: %v", err) @@ -93,7 +93,8 @@ func TestGenMdTree(t *testing.T) { } func BenchmarkGenMarkdownToFile(b *testing.B) { - file, err := os.CreateTemp("", "") + rootCmd, _, _, _, _, _, _ := getTestCmds() + file, err := os.CreateTemp(b.TempDir(), "") if err != nil { b.Fatal(err) } @@ -101,7 +102,7 @@ func BenchmarkGenMarkdownToFile(b *testing.B) { defer file.Close() b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { if err := doc.GenMarkdown(rootCmd, file); err != nil { b.Fatal(err) } diff --git a/doc/rest_docs.go b/doc/rest_docs.go index e41b6fa..416f675 100644 --- a/doc/rest_docs.go +++ b/doc/rest_docs.go @@ -26,7 +26,9 @@ import ( "github.com/zulucmd/zulu/v2" ) -func printOptionsReST(buf *bytes.Buffer, cmd *zulu.Command, name string) error { +type linkHandlerFn func(string, string) string + +func printOptionsReST(buf *bytes.Buffer, cmd *zulu.Command) error { flags := cmd.NonInheritedFlags() flags.SetOutput(buf) if flags.HasAvailableFlags() { @@ -47,7 +49,7 @@ func printOptionsReST(buf *bytes.Buffer, cmd *zulu.Command, name string) error { return nil } -// linkHandler for default ReST hyperlink markup +// linkHandler for default ReST hyperlink markup. func defaultLinkHandler(name, ref string) string { return fmt.Sprintf("`%s <%s.rst>`_", name, ref) } @@ -58,7 +60,7 @@ func GenReST(cmd *zulu.Command, w io.Writer) error { } // GenReSTCustom creates custom reStructured Text output. -func GenReSTCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string, string) string) error { +func GenReSTCustom(cmd *zulu.Command, w io.Writer, linkHandler linkHandlerFn) error { cmd.InitDefaultHelpCmd() cmd.InitDefaultHelpFlag() cmd.InitDefaultCompletionCmd() @@ -91,37 +93,10 @@ func GenReSTCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string, stri buf.WriteString(fmt.Sprintf("::\n\n%s\n\n", indentString(cmd.Example, " "))) } - if err := printOptionsReST(buf, cmd, name); err != nil { + if err := printOptionsReST(buf, cmd); err != nil { return err } - if hasSeeAlso(cmd) { - buf.WriteString("SEE ALSO\n") - buf.WriteString("~~~~~~~~\n\n") - if cmd.HasParent() { - parent := cmd.Parent() - pname := parent.CommandPath() - ref = strings.ReplaceAll(pname, " ", "_") - buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(pname, ref), parent.Short)) - cmd.VisitParents(func(c *zulu.Command) { - if c.DisableAutoGenTag { - cmd.DisableAutoGenTag = c.DisableAutoGenTag - } - }) - } - - children := cmd.Commands() - sort.Sort(byName(children)) - - for _, child := range children { - if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() { - continue - } - cname := name + " " + child.Name() - ref = strings.ReplaceAll(cname, " ", "_") - buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(cname, ref), child.Short)) - } - buf.WriteString("\n") - } + printSeeAlsoReST(cmd, buf, linkHandler, name) if !cmd.DisableAutoGenTag { buf.WriteString("*Auto generated by zulucmd/zulu on " + time.Now().Format("2-Jan-2006") + "*\n") } @@ -129,6 +104,40 @@ func GenReSTCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string, stri return err } +func printSeeAlsoReST(cmd *zulu.Command, buf *bytes.Buffer, linkHandler linkHandlerFn, name string) { + if !hasSeeAlso(cmd) { + return + } + + buf.WriteString("SEE ALSO\n") + buf.WriteString("~~~~~~~~\n\n") + if cmd.HasParent() { + parent := cmd.Parent() + pname := parent.CommandPath() + ref := strings.ReplaceAll(pname, " ", "_") + buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(pname, ref), parent.Short)) + cmd.VisitParents(func(c *zulu.Command) { + if c.DisableAutoGenTag { + cmd.DisableAutoGenTag = c.DisableAutoGenTag + } + }) + } + + children := cmd.Commands() + sort.Sort(byName(children)) + + for _, child := range children { + if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() { + continue + } + cname := name + " " + child.Name() + ref := strings.ReplaceAll(cname, " ", "_") + buf.WriteString(fmt.Sprintf("* %s \t - %s\n", linkHandler(cname, ref), child.Short)) + } + + buf.WriteString("\n") +} + // GenReSTTree will generate a ReST page for this command and all // descendants in the directory given. // This function may not work correctly if your command names have `-` in them. @@ -136,13 +145,18 @@ func GenReSTCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string, stri // and `sub` has a subcommand called `third`, it is undefined which // help output will be in the file `cmd-sub-third.1`. func GenReSTTree(cmd *zulu.Command, dir string) error { - emptyStr := func(s string) string { return "" } + emptyStr := func(_ string) string { return "" } return GenReSTTreeCustom(cmd, dir, emptyStr, defaultLinkHandler) } -// GenReSTTreeCustom is the the same as GenReSTTree, but +// GenReSTTreeCustom is the same as GenReSTTree, but // with custom filePrepender and linkHandler. -func GenReSTTreeCustom(cmd *zulu.Command, dir string, filePrepender func(string) string, linkHandler func(string, string) string) error { +func GenReSTTreeCustom( + cmd *zulu.Command, + dir string, + filePrepender func(string) string, + linkHandler linkHandlerFn, +) error { for _, c := range cmd.Commands() { if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { continue @@ -163,13 +177,11 @@ func GenReSTTreeCustom(cmd *zulu.Command, dir string, filePrepender func(string) if _, err := io.WriteString(f, filePrepender(filename)); err != nil { return err } - if err := GenReSTCustom(cmd, f, linkHandler); err != nil { - return err - } - return nil + + return GenReSTCustom(cmd, f, linkHandler) } -// adapted from: https://github.com/kr/text/blob/main/indent.go +// Adapted from: https://github.com/kr/text/blob/main/indent.go func indentString(s, p string) string { var res []byte b := []byte(s) diff --git a/doc/rest_docs_test.go b/doc/rest_docs_test.go index cc516c4..708acc3 100644 --- a/doc/rest_docs_test.go +++ b/doc/rest_docs_test.go @@ -8,9 +8,11 @@ import ( "github.com/zulucmd/zulu/v2" "github.com/zulucmd/zulu/v2/doc" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestGenRSTDoc(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() // We generate on a subcommand so we have both subcommands and parents buf := new(bytes.Buffer) if err := doc.GenReST(echoCmd, buf); err != nil { @@ -18,21 +20,21 @@ func TestGenRSTDoc(t *testing.T) { } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertNotContains(t, output, deprecatedCmd.Short) + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertNotContains(t, output, deprecatedCmd.Short) } func TestGenRSTNoHiddenParents(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, deprecatedCmd, _, _ := getTestCmds() // We generate on a subcommand so we have both subcommands and parents for _, name := range []string{"rootflag", "strtwo"} { f := rootCmd.PersistentFlags().Lookup(name) f.Hidden = true - defer func() { f.Hidden = false }() } buf := new(bytes.Buffer) if err := doc.GenReST(echoCmd, buf); err != nil { @@ -40,19 +42,19 @@ func TestGenRSTNoHiddenParents(t *testing.T) { } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertNotContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertNotContains(t, output, deprecatedCmd.Short) - assertNotContains(t, output, "Options inherited from parent commands") + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertNotContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertNotContains(t, output, deprecatedCmd.Short) + testutil.AssertNotContains(t, output, "Options inherited from parent commands") } func TestGenRSTNoTag(t *testing.T) { + rootCmd, _, _, _, _, _, _ := getTestCmds() rootCmd.DisableAutoGenTag = true - defer func() { rootCmd.DisableAutoGenTag = false }() buf := new(bytes.Buffer) if err := doc.GenReST(rootCmd, buf); err != nil { @@ -61,17 +63,13 @@ func TestGenRSTNoTag(t *testing.T) { output := buf.String() unexpected := "Auto generated" - assertNotContains(t, output, unexpected) + testutil.AssertNotContains(t, output, unexpected) } func TestGenRSTTree(t *testing.T) { c := &zulu.Command{Use: "do [OPTIONS] arg1 arg2"} - tmpdir, err := os.MkdirTemp("", "test-gen-rst-tree") - if err != nil { - t.Fatalf("Failed to create tmpdir: %s", err.Error()) - } - defer os.RemoveAll(tmpdir) + tmpdir := t.TempDir() if err := doc.GenReSTTree(c, tmpdir); err != nil { t.Fatalf("GenReSTTree failed: %s", err.Error()) @@ -83,7 +81,8 @@ func TestGenRSTTree(t *testing.T) { } func BenchmarkGenReSTToFile(b *testing.B) { - file, err := os.CreateTemp("", "") + rootCmd, _, _, _, _, _, _ := getTestCmds() + file, err := os.CreateTemp(b.TempDir(), "") if err != nil { b.Fatal(err) } @@ -91,7 +90,7 @@ func BenchmarkGenReSTToFile(b *testing.B) { defer file.Close() b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { if err := doc.GenReST(rootCmd, file); err != nil { b.Fatal(err) } diff --git a/doc/util.go b/doc/util.go index 675e020..ebe662e 100644 --- a/doc/util.go +++ b/doc/util.go @@ -39,7 +39,7 @@ func hasSeeAlso(cmd *zulu.Command) bool { // that do not contain \n. func forceMultiLine(s string) string { if len(s) > 60 && !strings.Contains(s, "\n") { - s = s + "\n" + s += "\n" } return s } diff --git a/doc/yaml_docs.go b/doc/yaml_docs.go index 2f86d77..9ab63a8 100644 --- a/doc/yaml_docs.go +++ b/doc/yaml_docs.go @@ -14,28 +14,26 @@ package doc import ( - "fmt" "io" "os" "path/filepath" "sort" "strings" + "github.com/zulucmd/zflag/v2" "github.com/zulucmd/zulu/v2" "gopkg.in/yaml.v3" - - "github.com/zulucmd/zflag/v2" ) type cmdOption struct { - Name string + Name string `yaml:"name"` Shorthand rune `yaml:",omitempty"` DefaultValue string `yaml:"default_value,omitempty"` Usage string `yaml:",omitempty"` } type cmdDoc struct { - Name string + Name string `yaml:"name"` Synopsis string `yaml:",omitempty"` Description string `yaml:",omitempty"` Usage string `yaml:",omitempty"` @@ -52,7 +50,7 @@ type cmdDoc struct { // it is undefined which help output will be in the file `cmd-sub-third.1`. func GenYamlTree(cmd *zulu.Command, dir string) error { identity := func(s string) string { return s } - emptyStr := func(s string) string { return "" } + emptyStr := func(_ string) string { return "" } return GenYamlTreeCustom(cmd, dir, emptyStr, identity) } @@ -78,10 +76,8 @@ func GenYamlTreeCustom(cmd *zulu.Command, dir string, filePrepender, linkHandler if _, err := io.WriteString(f, filePrepender(filename)); err != nil { return err } - if err := GenYamlCustom(cmd, f, linkHandler); err != nil { - return err - } - return nil + + return GenYamlCustom(cmd, f, linkHandler) } // GenYaml creates yaml output. @@ -119,7 +115,7 @@ func GenYamlCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) stri } if hasSeeAlso(cmd) { - result := []string{} + var result []string if cmd.HasParent() { parent := cmd.Parent() result = append(result, parent.CommandPath()+" - "+parent.Short) @@ -137,14 +133,12 @@ func GenYamlCustom(cmd *zulu.Command, w io.Writer, linkHandler func(string) stri final, err := yaml.Marshal(&yamlDoc) if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if _, err := w.Write(final); err != nil { return err } - return nil + + _, err = w.Write(final) + + return err } func genFlagResult(flags *zflag.FlagSet) []cmdOption { diff --git a/doc/yaml_docs_test.go b/doc/yaml_docs_test.go index 64fa69b..6255910 100644 --- a/doc/yaml_docs_test.go +++ b/doc/yaml_docs_test.go @@ -9,9 +9,11 @@ import ( "github.com/zulucmd/zulu/v2" "github.com/zulucmd/zulu/v2/doc" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestGenYamlDoc(t *testing.T) { + rootCmd, echoCmd, echoSubCmd, _, _, _, _ := getTestCmds() // We generate on s subcommand so we have both subcommands and parents buf := new(bytes.Buffer) if err := doc.GenYaml(echoCmd, buf); err != nil { @@ -19,18 +21,18 @@ func TestGenYamlDoc(t *testing.T) { } output := buf.String() - assertContains(t, output, echoCmd.Long) - assertContains(t, output, echoCmd.Example) - assertContains(t, output, "boolone") - assertContains(t, output, "rootflag") - assertContains(t, output, rootCmd.Short) - assertContains(t, output, echoSubCmd.Short) - assertContains(t, output, fmt.Sprintf("- %s - %s", echoSubCmd.CommandPath(), echoSubCmd.Short)) + testutil.AssertContains(t, output, echoCmd.Long) + testutil.AssertContains(t, output, echoCmd.Example) + testutil.AssertContains(t, output, "boolone") + testutil.AssertContains(t, output, "rootflag") + testutil.AssertContains(t, output, rootCmd.Short) + testutil.AssertContains(t, output, echoSubCmd.Short) + testutil.AssertContains(t, output, fmt.Sprintf("- %s - %s", echoSubCmd.CommandPath(), echoSubCmd.Short)) } func TestGenYamlNoTag(t *testing.T) { + rootCmd, _, _, _, _, _, _ := getTestCmds() rootCmd.DisableAutoGenTag = true - defer func() { rootCmd.DisableAutoGenTag = false }() buf := new(bytes.Buffer) if err := doc.GenYaml(rootCmd, buf); err != nil { @@ -38,17 +40,13 @@ func TestGenYamlNoTag(t *testing.T) { } output := buf.String() - assertNotContains(t, output, "Auto generated") + testutil.AssertNotContains(t, output, "Auto generated") } func TestGenYamlTree(t *testing.T) { c := &zulu.Command{Use: "do [OPTIONS] arg1 arg2"} - tmpdir, err := os.MkdirTemp("", "test-gen-yaml-tree") - if err != nil { - t.Fatalf("Failed to create tmpdir: %s", err.Error()) - } - defer os.RemoveAll(tmpdir) + tmpdir := t.TempDir() if err := doc.GenYamlTree(c, tmpdir); err != nil { t.Fatalf("GenYamlTree failed: %s", err.Error()) @@ -60,6 +58,7 @@ func TestGenYamlTree(t *testing.T) { } func TestGenYamlDocRunnable(t *testing.T) { + rootCmd, _, _, _, _, _, _ := getTestCmds() // Testing a runnable command: should contain the "usage" field buf := new(bytes.Buffer) if err := doc.GenYaml(rootCmd, buf); err != nil { @@ -67,11 +66,12 @@ func TestGenYamlDocRunnable(t *testing.T) { } output := buf.String() - assertContains(t, output, "usage: "+rootCmd.Use) + testutil.AssertContains(t, output, "usage: "+rootCmd.Use) } func BenchmarkGenYamlToFile(b *testing.B) { - file, err := os.CreateTemp("", "") + rootCmd, _, _, _, _, _, _ := getTestCmds() + file, err := os.CreateTemp(b.TempDir(), "") if err != nil { b.Fatal(err) } @@ -79,7 +79,7 @@ func BenchmarkGenYamlToFile(b *testing.B) { defer file.Close() b.ResetTimer() - for i := 0; i < b.N; i++ { + for range b.N { if err := doc.GenYaml(rootCmd, file); err != nil { b.Fatal(err) } diff --git a/exports_test.go b/exports_test.go index b391d7a..bd7e8ae 100644 --- a/exports_test.go +++ b/exports_test.go @@ -1,3 +1,4 @@ +//nolint:testpackage // this is explicitly design to expose some functions in tests package zulu const ( diff --git a/fish_completions_test.go b/fish_completions_test.go index 17105e2..2c7e2ac 100644 --- a/fish_completions_test.go +++ b/fish_completions_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestCompleteNoDesCmdInFishScript(t *testing.T) { @@ -20,10 +21,10 @@ func TestCompleteNoDesCmdInFishScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenFishCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenFishCompletion(buf, false)) output := buf.String() - assertContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompNoDescRequestCmd) } func TestCompleteCmdInFishScript(t *testing.T) { @@ -36,45 +37,45 @@ func TestCompleteCmdInFishScript(t *testing.T) { rootCmd.AddCommand(child) buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenFishCompletion(buf, true)) + testutil.AssertNil(t, rootCmd.GenFishCompletion(buf, true)) output := buf.String() - assertContains(t, output, zulu.ShellCompRequestCmd+" ") - assertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) + testutil.AssertContains(t, output, zulu.ShellCompRequestCmd+" ") + testutil.AssertNotContains(t, output, zulu.ShellCompNoDescRequestCmd) } func TestProgWithDash(t *testing.T) { rootCmd := &zulu.Command{Use: "root-dash", Args: zulu.NoArgs, RunE: noopRun} buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenFishCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenFishCompletion(buf, false)) output := buf.String() // Functions name should have replaced the '-' - assertContains(t, output, "__root_dash_perform_completion") - assertNotContains(t, output, "__root-dash_perform_completion") + testutil.AssertContains(t, output, "__root_dash_perform_completion") + testutil.AssertNotContains(t, output, "__root-dash_perform_completion") // The command name should not have replaced the '-' - assertContains(t, output, "-c root-dash") - assertNotContains(t, output, "-c root_dash") + testutil.AssertContains(t, output, "-c root-dash") + testutil.AssertNotContains(t, output, "-c root_dash") } func TestProgWithColon(t *testing.T) { rootCmd := &zulu.Command{Use: "root:colon", Args: zulu.NoArgs, RunE: noopRun} buf := new(bytes.Buffer) - assertNil(t, rootCmd.GenFishCompletion(buf, false)) + testutil.AssertNil(t, rootCmd.GenFishCompletion(buf, false)) output := buf.String() // Functions name should have replaced the ':' - assertContains(t, output, "__root_colon_perform_completion") - assertNotContains(t, output, "__root:colon_perform_completion") + testutil.AssertContains(t, output, "__root_colon_perform_completion") + testutil.AssertNotContains(t, output, "__root:colon_perform_completion") // The command name should not have replaced the ':' - assertContains(t, output, "-c root:colon") - assertNotContains(t, output, "-c root_colon") + testutil.AssertContains(t, output, "-c root:colon") + testutil.AssertNotContains(t, output, "-c root_colon") } func TestGenFishCompletionFile(t *testing.T) { - tmpFile, err := os.CreateTemp("", "cobra-test") + tmpFile, err := os.CreateTemp(t.TempDir(), "cobra-test") if err != nil { t.Fatal(err.Error()) } @@ -88,15 +89,11 @@ func TestGenFishCompletionFile(t *testing.T) { } rootCmd.AddCommand(child) - assertNil(t, rootCmd.GenFishCompletionFile(tmpFile.Name(), false)) + testutil.AssertNil(t, rootCmd.GenFishCompletionFile(tmpFile.Name(), false)) } func TestFailGenFishCompletionFile(t *testing.T) { - tmpDir, err := os.MkdirTemp("", "cobra-test") - if err != nil { - t.Fatal(err.Error()) - } - defer os.RemoveAll(tmpDir) + tmpDir := t.TempDir() f, _ := os.OpenFile(filepath.Join(tmpDir, "test"), os.O_CREATE, 0400) defer f.Close() @@ -110,6 +107,6 @@ func TestFailGenFishCompletionFile(t *testing.T) { rootCmd.AddCommand(child) got := rootCmd.GenFishCompletionFile(f.Name(), false) - assertNotNilf(t, got, "should raise permission denied error") - assertEqual(t, true, errors.Is(got, os.ErrPermission)) + testutil.AssertNotNilf(t, got, "should raise permission denied error") + testutil.AssertEqual(t, true, errors.Is(got, os.ErrPermission)) } diff --git a/flag_groups.go b/flag_groups.go index 5f6c7c4..685ee86 100644 --- a/flag_groups.go +++ b/flag_groups.go @@ -171,7 +171,7 @@ func (s setFlagsSet) selectSetFlagNamesFrom(flagNames []string) (setFlagNames [] setFlagNames = append(setFlagNames, flagName) } } - return + return setFlagNames } func (s setFlagsSet) selectUnsetFlagNamesFrom(flagNames []string) (unsetFlagNames []string) { for _, flagName := range flagNames { @@ -179,5 +179,5 @@ func (s setFlagsSet) selectUnsetFlagNamesFrom(flagNames []string) (unsetFlagName unsetFlagNames = append(unsetFlagNames, flagName) } } - return + return unsetFlagNames } diff --git a/flag_groups_test.go b/flag_groups_test.go index 1c2bcf9..ba74a14 100644 --- a/flag_groups_test.go +++ b/flag_groups_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/zulucmd/zulu/v2" + "github.com/zulucmd/zulu/v2/internal/testutil" ) func TestValidateFlagGroups(t *testing.T) { @@ -111,7 +112,6 @@ func TestValidateFlagGroups(t *testing.T) { } for _, tc := range testcases { - tc := tc t.Run(tc.desc, func(t *testing.T) { t.Parallel() @@ -153,12 +153,12 @@ func TestValidateFlagGroups(t *testing.T) { err := cmd.Execute() if len(tc.expectErr) > 0 { - assertNotNilf(t, err, "Expected an error") - assertEqual(t, tc.expectErr, err.Error()) + testutil.AssertNotNilf(t, err, "Expected an error") + testutil.AssertEqual(t, tc.expectErr, err.Error()) return } - assertNilf(t, err, "Unexpected error") + testutil.AssertNilf(t, err, "Unexpected error") }) } } diff --git a/internal/enumer/enumer.go b/internal/enumer/enumer.go index 65e9033..02daa11 100644 --- a/internal/enumer/enumer.go +++ b/internal/enumer/enumer.go @@ -5,6 +5,7 @@ package main import ( "bytes" + "errors" "flag" "fmt" "go/ast" @@ -12,7 +13,6 @@ import ( gofmt "go/format" "go/token" "go/types" - "io/ioutil" "log" "os" "path/filepath" @@ -22,16 +22,12 @@ import ( "golang.org/x/tools/go/packages" ) -var ( - typeName = flag.String("type", "", "comma-separated list of type names; must be set") - output = flag.String("output", "", "output file name; default srcdir/_string.go") - templateFile = flag.String("template", "", "template file to use") - format = flag.Bool("format", false, "format the template, only for code generation") -) - // Usage is a replacement usage function for the flags package. func Usage() { - _, _ = fmt.Fprintf(os.Stderr, "Enumer is a tool to generate files based on Go enums (constants with a specific type).\n") + _, _ = fmt.Fprintf( + os.Stderr, + "Enumer is a tool to generate files based on Go enums (constants with a specific type).\n", + ) _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0]) _, _ = fmt.Fprintf(os.Stderr, "\tEnumer [flags] -type T [directory]\n") _, _ = fmt.Fprintf(os.Stderr, "\tEnumer [flags] -type T files... # Must be a single package\n") @@ -44,8 +40,22 @@ func Usage() { func main() { log.SetFlags(0) log.SetPrefix("enumer: ") - flag.Usage = Usage - flag.Parse() + + fs := flag.NewFlagSet("enumer", flag.ContinueOnError) + typeName := fs.String("type", "", "comma-separated list of type names; must be set") + output := fs.String("output", "", "output file name; default srcdir/_string.go") + templateFile := fs.String("template", "", "template file to use") + format := fs.Bool("format", false, "format the template, only for code generation") + fs.Usage = Usage + err := fs.Parse(os.Args[1:]) + if err != nil { + if errors.Is(err, flag.ErrHelp) { + // usage is already printed + os.Exit(0) + } + os.Exit(1) + } + if len(*typeName) == 0 { flag.Usage() os.Exit(2) @@ -61,37 +71,51 @@ func main() { // Parse the package once. var g Generator - dir := getDir(args[0]) + dir, err := getDir(args[0]) + if err != nil { + log.Fatal(err) + } + path, err := filepath.Rel(dir, *templateFile) if err != nil { - panic(err) + log.Fatal(err) } - g.parsePackage(args) + err = g.parsePackage(args) + if err != nil { + log.Fatal(err) + } - res, err := template.ParseFromFile(os.DirFS(dir), path, map[string]interface{}{ + values, err := g.getValues(*typeName) + if err != nil { + log.Fatal(err) + } + + res, err := template.ParseFromFile(os.DirFS(dir), path, map[string]any{ "pkgName": g.pkg.name, "args": strings.Join(os.Args[1:], " "), "typeName": *typeName, - "values": g.getValues(*typeName), + "values": values, }, nil) if err != nil { - panic(err) + log.Fatal(err) } g.Print(res) src := g.format(*format) - writeSource(*typeName, dir, *output, src) + if err = writeSource(*typeName, dir, *output, src); err != nil { + log.Fatal(err) + } } -func writeSource(typeName, dir, outputName string, src []byte) { +func writeSource(typeName, dir, outputName string, src []byte) error { if outputName == "-" { _, err := os.Stdout.Write(src) if err != nil { - log.Fatalf("failed to write output: %s", err) + return fmt.Errorf("failed to write output: %w", err) } - return + return nil } if outputName == "" { @@ -100,36 +124,39 @@ func writeSource(typeName, dir, outputName string, src []byte) { } // Write to tmpfile first - tmpFile, err := ioutil.TempFile(dir, fmt.Sprintf("%s_enumer_", typeName)) + tmpFile, err := os.CreateTemp(dir, fmt.Sprintf("%s_enumer_", typeName)) if err != nil { - log.Fatalf("creating temporary file for output: %s", err) + return fmt.Errorf("creating temporary file for output: %w", err) } + _, err = tmpFile.Write(src) if err != nil { - tmpFile.Close() - os.Remove(tmpFile.Name()) - log.Fatalf("failed to write output: %s", err) + _ = tmpFile.Close() + _ = os.Remove(tmpFile.Name()) + return fmt.Errorf("failed to write output: %w", err) } - tmpFile.Close() + _ = tmpFile.Close() // Rename tmpfile to output file err = os.Rename(tmpFile.Name(), outputName) if err != nil { - log.Fatalf("failed to move tempfile to output file: %s", err) + return fmt.Errorf("failed to move tempfile to output file: %w", err) } + + return nil } -func getDir(fileOrDir string) string { +func getDir(fileOrDir string) (string, error) { info, err := os.Stat(fileOrDir) if err != nil { - log.Fatal(err) + return "", err } if info.IsDir() { - return fileOrDir + return fileOrDir, nil } - return filepath.Dir(fileOrDir) + return filepath.Dir(fileOrDir), nil } // Generator holds the state of the analysis. Primarily used to buffer @@ -139,12 +166,12 @@ type Generator struct { pkg *Package // Package we are scanning. } -// Printf prints the string to the output -func (g *Generator) Printf(format string, args ...interface{}) { +// Printf prints the string to the output. +func (g *Generator) Printf(format string, args ...any) { _, _ = fmt.Fprintf(&g.buf, format, args...) } -// Print prints the string to the output +// Print prints the string to the output. func (g *Generator) Print(str string) { _, _ = fmt.Fprint(&g.buf, str) } @@ -156,9 +183,10 @@ type File struct { // These fields are reset for each type being generated. typeName string // Name of the constant type. values []Value // Accumulator for constant values of that type. + err error // Stores any error encountered during processing } -// Package holds information about a Go package +// Package holds information about a Go package. type Package struct { name string defs map[*ast.Ident]types.Object @@ -166,8 +194,7 @@ type Package struct { } // parsePackage analyzes the single package constructed from the patterns and tags. -// parsePackage exits if there is an error. -func (g *Generator) parsePackage(patterns []string) { +func (g *Generator) parsePackage(patterns []string) error { cfg := &packages.Config{ Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | @@ -176,12 +203,15 @@ func (g *Generator) parsePackage(patterns []string) { } pkgs, err := packages.Load(cfg, patterns...) if err != nil { - log.Fatal(err) + return err } if len(pkgs) != 1 { - log.Fatalf("error: %d packages found", len(pkgs)) + return fmt.Errorf("error: %d packages found", len(pkgs)) } + g.addPackage(pkgs[0]) + + return nil } // addPackage adds a type checked Package and its syntax files to the generator. @@ -201,22 +231,26 @@ func (g *Generator) addPackage(pkg *packages.Package) { } // getValues produces the String method for the named type. -func (g *Generator) getValues(typeName string) []Value { +func (g *Generator) getValues(typeName string) ([]Value, error) { values := make([]Value, 0, 100) for _, file := range g.pkg.files { file.typeName = typeName file.values = nil + file.err = nil // Reset any previous error if file.file != nil { ast.Inspect(file.file, file.genDecl) + if file.err != nil { + return nil, file.err + } values = append(values, file.values...) } } if len(values) == 0 { - log.Fatalf("no values defined for type %s", typeName) + return nil, fmt.Errorf("no values defined for type %s", typeName) } - return values + return values, nil } // format returns the gofmt-ed contents of the Generator's buffer. @@ -255,7 +289,72 @@ func (v *Value) String() string { return v.Value } +// processConstant handles the processing of a single constant value. +func (f *File) processConstant(n *ast.Ident, vspec *ast.ValueSpec, typ string) (*Value, error) { + // This dance lets the type checker find the values for us. It's a + // bit tricky: look up the object declared by the n, find its + // types.Const, and extract its value. + obj, ok := f.pkg.defs[n] + if !ok { + return nil, fmt.Errorf("no value for constant %s", n) + } + + underlying, ok := obj.Type().Underlying().(*types.Basic) + if !ok { + return nil, fmt.Errorf("can't handle non-basic underlying type %v", n) + } + + info := underlying.Info() + if info&types.IsInteger == 0 { + return nil, fmt.Errorf("can't handle non-integer constant type %s", typ) + } + + c, ok := obj.(*types.Const) + if !ok { + return nil, fmt.Errorf("can't happen: value is not constant %v", n) + } + + value := c.Val() + if c.Val().Kind() != exact.Int { + return nil, fmt.Errorf("can't happen: constant is not an integer %s", n) + } + + v := &Value{ + Name: n.Name, + Value: value.String(), + Exported: n.IsExported(), + } + + if err := processComments(v, vspec, n); err != nil { + return nil, err + } + + return v, nil +} + +// processComments handles the extraction and validation of comments. +func processComments(v *Value, vspec *ast.ValueSpec, n *ast.Ident) error { + if vspec.Comment != nil && vspec.Doc != nil { + return fmt.Errorf("cannot work with both doc comment and normal comment: %s", n.Name) + } + + if vspec.Comment != nil || vspec.Doc != nil { + var comment *ast.CommentGroup + switch { + case vspec.Comment == nil && vspec.Doc != nil: + comment = vspec.Doc + case vspec.Comment != nil && vspec.Doc == nil: + comment = vspec.Comment + } + v.Comment = getComment(comment.List) + } + + return nil +} + // genDecl processes one declaration clause. +// +//nolint:gocognit // will refactor later func (f *File) genDecl(node ast.Node) bool { decl, ok := node.(*ast.GenDecl) if !ok || decl.Tok != token.CONST { @@ -270,7 +369,7 @@ func (f *File) genDecl(node ast.Node) bool { // If the type and value are both missing, we carry down the type (and value, // but the "go/types" package takes care of that). for _, spec := range decl.Specs { - vspec := spec.(*ast.ValueSpec) // Guaranteed to succeed as this is CONST. + vspec := spec.(*ast.ValueSpec) //nolint:errcheck // Guaranteed to succeed as this is CONST. if vspec.Type == nil && len(vspec.Values) > 0 { // "X = 1". With no type but a value, the constant is untyped. // Skip this vspec and reset the remembered type. @@ -297,52 +396,20 @@ func (f *File) genDecl(node ast.Node) bool { continue } - // This dance lets the type checker find the values for us. It's a - // bit tricky: look up the object declared by the n, find its - // types.Const, and extract its value. - obj, ok := f.pkg.defs[n] - if !ok { - log.Fatalf("no value for constant %s", n) - } - - info := obj.Type().Underlying().(*types.Basic).Info() - if info&types.IsInteger == 0 { - log.Fatalf("can't handle non-integer constant type %s", typ) - } - - value := obj.(*types.Const).Val() // Guaranteed to succeed as this is CONST. - if value.Kind() != exact.Int { - log.Fatalf("can't happen: constant is not an integer %s", n) - } - - v := Value{ - Name: n.Name, - Value: value.String(), - Exported: n.IsExported(), - } - - if vspec.Comment != nil || vspec.Doc != nil { - var comment *ast.CommentGroup - switch { - case vspec.Comment == nil && vspec.Doc != nil: - comment = vspec.Doc - case vspec.Comment != nil && vspec.Doc == nil: - comment = vspec.Comment - default: - log.Fatalf("cannot work with both doc comment and normal comment: %s", n.Name) - } - - v.Comment = getComment(comment.List) + value, err := f.processConstant(n, vspec, typ) + if err != nil { + f.err = err + return false } - f.values = append(f.values, v) + f.values = append(f.values, *value) } } return false } func getComment(commentList []*ast.Comment) string { - var comment []byte + var comment []byte //nolint:prealloc // we don't have the total size for _, c := range commentList { comment = append(comment, c.Text...) comment = append(comment, '\n') diff --git a/internal/template/template.go b/internal/template/template.go index aa886b4..70822f5 100644 --- a/internal/template/template.go +++ b/internal/template/template.go @@ -8,28 +8,28 @@ import ( "text/template" ) -func ParseFromFile(fs fs.FS, templateFile string, data interface{}, funcs template.FuncMap) (string, error) { - f, err := fs.Open(templateFile) +func ParseFromFile(fsys fs.FS, templateFile string, data any, funcs template.FuncMap) (string, error) { + f, err := fsys.Open(templateFile) if err != nil { - return "", fmt.Errorf("template: failed to open template file %q: %s", templateFile, err) + return "", fmt.Errorf("template: failed to open template file %q: %w", templateFile, err) } templateData, err := io.ReadAll(f) if err != nil { - return "", fmt.Errorf("template: failed to read template file %q: %s", templateFile, err) + return "", fmt.Errorf("template: failed to read template file %q: %w", templateFile, err) } buf := new(bytes.Buffer) err = Parse(buf, string(templateData), data, funcs) if err != nil { - return "", fmt.Errorf("template: failed to parse template: %s", err) + return "", fmt.Errorf("template: failed to parse template: %w", err) } return buf.String(), nil } // Parse executes the given template text on data, writing the result to w. -func Parse(w io.Writer, text string, data interface{}, funcs template.FuncMap) error { +func Parse(w io.Writer, text string, data any, funcs template.FuncMap) error { t := template.New("top") t.Funcs(funcs) template.Must(t.Parse(text)) diff --git a/internal/testutil/assert.go b/internal/testutil/assert.go new file mode 100644 index 0000000..a9b9ab2 --- /dev/null +++ b/internal/testutil/assert.go @@ -0,0 +1,99 @@ +package testutil + +import ( + "bytes" + "encoding/json" + "fmt" + "regexp" + "strings" + "testing" +) + +func AssertNotContains(t *testing.T, str, unexpected string) { + t.Helper() + AssertNotContainsf(t, str, unexpected, "%q should not contain %q", str, unexpected) +} + +func AssertNotContainsf(t *testing.T, str, unexpected string, msg string, fmt ...any) { + t.Helper() + if strings.Contains(str, unexpected) { + t.Errorf(msg, fmt...) + } +} + +func AssertContains(t *testing.T, str, substr string) { + t.Helper() + fmt := "Expected %q to contain %q" + if len(str) > 100 { + fmt = "Expected:\n---\n%s\n---\n\nto contain:\n%s" + } + AssertContainsf(t, str, substr, fmt, str, substr) +} + +func AssertContainsf(t *testing.T, str, expected string, msg string, fmt ...any) { + t.Helper() + if !strings.Contains(str, expected) { + t.Errorf(msg, fmt...) + } +} + +func toStr(t *testing.T, obj any) string { + t.Helper() + switch o := obj.(type) { + case string: + return o + case error: + return o.Error() + case fmt.Stringer: + return o.String() + case []byte: + return string(o) + default: + buf := bytes.Buffer{} + enc := json.NewEncoder(&buf) + enc.SetIndent("", " ") + err := enc.Encode(obj) + if err != nil { + t.Fatalf("failed to convert %+v to string", obj) + } + return buf.String() + } +} + +func AssertNotEqualf(t *testing.T, unexpected, actual any, msg string, f ...any) { + t.Helper() + if actual == unexpected { + diff := Diff([]byte(toStr(t, unexpected)), []byte(toStr(t, actual))) + t.Errorf("%[1]s\nUnexpected type %[2]T, actual type %[3]T\n%[4]s", fmt.Sprintf(msg, f...), unexpected, actual, diff) + } +} + +func AssertEqual(t *testing.T, expected, actual any) { + t.Helper() + AssertEqualf(t, expected, actual, "Values are not equal.") +} + +func AssertEqualf(t *testing.T, expected, actual any, msg string, f ...any) { + t.Helper() + if expected != actual { + diff := Diff([]byte(toStr(t, expected)), []byte(toStr(t, actual))) + t.Errorf("%[1]s\nExpected type %[2]T, actual type %[3]T\n%[4]s", fmt.Sprintf(msg, f...), expected, actual, diff) + } +} + +func AssertErrf(t *testing.T, e error, msg string, f ...any) { + t.Helper() + if e == nil { + if msg != "" { + msg = ": " + msg + } + t.Errorf("expected an error but got none"+msg, f...) + } +} + +func AssertMatch(t *testing.T, str, pattern string) { + t.Helper() + if ok, _ := regexp.MatchString(pattern, str); !ok { + t.Errorf("Expected to match: \n%v\nGot:\n %v\n", pattern, str) + } +} diff --git a/diff_test.go b/internal/testutil/diff.go similarity index 98% rename from diff_test.go rename to internal/testutil/diff.go index 61f73ea..56949ef 100644 --- a/diff_test.go +++ b/internal/testutil/diff.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Copied from golang.org/x/build/internal/diff -package zulu_test +//nolint:gocognit,gocritic,funlen,predeclared // Copied from golang.org/x/build/internal/diff +package testutil import ( "bytes" @@ -232,7 +232,7 @@ func tgs(x, y []string) []pair { for i := range T { T[i] = n + 1 } - for i := 0; i < n; i++ { + for i := range n { k := sort.Search(n, func(k int) bool { return T[k] >= J[i] }) diff --git a/internal/testutil/nil.go b/internal/testutil/nil.go new file mode 100644 index 0000000..d313a3a --- /dev/null +++ b/internal/testutil/nil.go @@ -0,0 +1,49 @@ +package testutil + +import ( + "reflect" + "testing" +) + +func IsNil(i any) bool { + if i == nil { + return true + } + + //nolint:exhaustive // default clause captures the rest + switch reflect.TypeOf(i).Kind() { + case reflect.Pointer, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: + return reflect.ValueOf(i).IsNil() + default: + return false + } +} + +func AssertNotNilf(t *testing.T, obj any, msg string, f ...any) { + t.Helper() + if IsNil(obj) { + if msg == "" { + t.Errorf("expected a value but got %v", obj) + return + } + + t.Errorf(msg, f...) + } +} + +func AssertNilf(t *testing.T, obj any, msg string, f ...any) { + t.Helper() + if !IsNil(obj) { + if msg == "" { + t.Errorf("expected nil but got %v", obj) + return + } + + t.Errorf(msg, f...) + } +} + +func AssertNil(t *testing.T, e error) { + t.Helper() + AssertNilf(t, e, "") +} diff --git a/internal/util/util.go b/internal/util/util.go index 1ffd1f9..7ad8974 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -6,7 +6,7 @@ import ( // CheckErr prints the msg with the prefix 'panic:' and a stack trace, and exits with error code 1. // If is not of type error or string, or if it's an empty string, it does nothing. -func CheckErr(msg interface{}) { +func CheckErr(msg any) { switch m := msg.(type) { case string, error: if m != "" { diff --git a/internal/util/util_test.go b/internal/util/util_test.go index 47aadb7..77a35eb 100644 --- a/internal/util/util_test.go +++ b/internal/util/util_test.go @@ -10,7 +10,7 @@ import ( func TestCheckErr(t *testing.T) { tests := []struct { name string - msg interface{} + msg any panic bool }{ { @@ -42,7 +42,6 @@ func TestCheckErr(t *testing.T) { t.Parallel() for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() diff --git a/powershell_completions.go b/powershell_completions.go index 0526e7b..6becbe6 100644 --- a/powershell_completions.go +++ b/powershell_completions.go @@ -4,7 +4,6 @@ package zulu import ( - _ "embed" "io" "os" ) diff --git a/shell_completions.go b/shell_completions.go index 0a841ac..3c8615d 100644 --- a/shell_completions.go +++ b/shell_completions.go @@ -19,13 +19,13 @@ func FlagOptDirname(dirnames ...string) zflag.Opt { } // FlagOptCompletionFunc is used to register a function to provide completion for a flag. -func FlagOptCompletionFunc(f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) zflag.Opt { +func FlagOptCompletionFunc(f FlagCompletionFn) zflag.Opt { return func(flag *zflag.Flag) error { flagCompletionMutex.Lock() defer flagCompletionMutex.Unlock() if _, exists := flagCompletionFunctions[flag]; exists { - return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flag.Name) + return fmt.Errorf("flag '%s' already registered", flag.Name) } flagCompletionFunctions[flag] = f diff --git a/site/content/code/shell_directives.gen.txt b/site/content/code/shell_directives.gen.txt index bdd5aaa..15080a1 100644 --- a/site/content/code/shell_directives.gen.txt +++ b/site/content/code/shell_directives.gen.txt @@ -32,7 +32,7 @@ ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs // ShellCompDirectiveKeepOrder indicates that the shell should preserve the order -// in which the completions are provided +// in which the completions are provided. ShellCompDirectiveKeepOrder // ShellCompDirectiveDefault indicates to let the shell perform its default diff --git a/site/content/docgen/adoc.md b/site/content/docgen/adoc.md index 8de7c1f..d95b396 100644 --- a/site/content/docgen/adoc.md +++ b/site/content/docgen/adoc.md @@ -39,7 +39,7 @@ package main import ( "log" - "io/ioutil" + "io" "os" "k8s.io/kubernetes/pkg/kubectl/cmd" @@ -49,7 +49,7 @@ import ( ) func main() { - kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard) err := doc.GenAsciidocTree(kubectl, "./") if err != nil { log.Fatal(err) diff --git a/site/content/docgen/md.md b/site/content/docgen/md.md index cd0cc76..1120c62 100644 --- a/site/content/docgen/md.md +++ b/site/content/docgen/md.md @@ -39,7 +39,7 @@ package main import ( "log" - "io/ioutil" + "io" "os" "k8s.io/kubernetes/pkg/kubectl/cmd" @@ -49,7 +49,7 @@ import ( ) func main() { - kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard) err := doc.GenMarkdownTree(kubectl, "./") if err != nil { log.Fatal(err) diff --git a/site/content/docgen/rest.md b/site/content/docgen/rest.md index 206d4c5..9a45268 100644 --- a/site/content/docgen/rest.md +++ b/site/content/docgen/rest.md @@ -39,7 +39,7 @@ package main import ( "log" - "io/ioutil" + "io" "os" "k8s.io/kubernetes/pkg/kubectl/cmd" @@ -49,7 +49,7 @@ import ( ) func main() { - kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard) err := doc.GenReSTTree(kubectl, "./") if err != nil { log.Fatal(err) diff --git a/site/content/docgen/yaml.md b/site/content/docgen/yaml.md index fe8efb5..71540a5 100644 --- a/site/content/docgen/yaml.md +++ b/site/content/docgen/yaml.md @@ -38,7 +38,7 @@ This program can actually generate docs for the kubectl command in the kubernete package main import ( - "io/ioutil" + "io" "log" "os" @@ -49,7 +49,7 @@ import ( ) func main() { - kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard) + kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, io.Discard, io.Discard) err := doc.GenYamlTree(kubectl, "./") if err != nil { log.Fatal(err) diff --git a/zsh_completions.go b/zsh_completions.go index c7c7b24..5e665bf 100644 --- a/zsh_completions.go +++ b/zsh_completions.go @@ -1,7 +1,6 @@ package zulu import ( - _ "embed" "io" "os" ) diff --git a/zulu.go b/zulu.go index 77489c6..ada95ea 100644 --- a/zulu.go +++ b/zulu.go @@ -57,7 +57,7 @@ var MousetrapDisplayDuration = 5 * time.Second // AddTemplateFunc adds a template function that's available to Usage and Help // template generation. -func AddTemplateFunc(name string, tmplFunc interface{}) { +func AddTemplateFunc(name string, tmplFunc any) { templateFuncs[name] = tmplFunc } @@ -79,8 +79,8 @@ func rpad(s string, padding int) string { return fmt.Sprintf(format, s) } -// ld compares two strings and returns the levenshtein distance between them. -func ld(s, t string, ignoreCase bool) int { +// calculateLevenshteinDistance compares two strings and returns the levenshtein distance between them. +func calculateLevenshteinDistance(s, t string, ignoreCase bool) int { if ignoreCase { s = strings.ToLower(s) t = strings.ToLower(t) @@ -100,17 +100,16 @@ func ld(s, t string, ignoreCase bool) int { if s[i-1] == t[j-1] { d[i][j] = d[i-1][j-1] } else { - min := d[i-1][j] - if d[i][j-1] < min { - min = d[i][j-1] + lowest := d[i-1][j] + if d[i][j-1] < lowest { + lowest = d[i][j-1] } - if d[i-1][j-1] < min { - min = d[i-1][j-1] + if d[i-1][j-1] < lowest { + lowest = d[i-1][j-1] } - d[i][j] = min + 1 + d[i][j] = lowest + 1 } } - } return d[len(s)][len(t)] } diff --git a/zulu_test.go b/zulu_test.go index 15bdca5..37125b7 100644 --- a/zulu_test.go +++ b/zulu_test.go @@ -1,136 +1,12 @@ package zulu_test import ( - "bytes" - "encoding/json" - "fmt" - "reflect" - "strings" "testing" "text/template" "github.com/zulucmd/zulu/v2" ) -func assertNotContains(t *testing.T, str, unexpected string) { - t.Helper() - assertNotContainsf(t, str, unexpected, "%q should not contain %q", str, unexpected) -} - -func assertNotContainsf(t *testing.T, str, unexpected string, msg string, fmt ...interface{}) { - t.Helper() - if strings.Contains(str, unexpected) { - t.Errorf(msg, fmt...) - } -} - -func assertContains(t *testing.T, str, substr string) { - t.Helper() - assertContainsf(t, str, substr, "%q does not contain %q", str, substr) -} - -func assertContainsf(t *testing.T, str, expected string, msg string, fmt ...interface{}) { - t.Helper() - if !strings.Contains(str, expected) { - t.Errorf(msg, fmt...) - } -} - -func toStr(t *testing.T, obj interface{}) string { - t.Helper() - switch o := obj.(type) { - case string: - return o - case error: - return o.Error() - case fmt.Stringer: - return o.String() - case []byte: - return string(o) - default: - buf := bytes.Buffer{} - enc := json.NewEncoder(&buf) - enc.SetIndent("", " ") - err := enc.Encode(obj) - if err != nil { - t.Fatalf("failed to convert %+v to string", obj) - } - return buf.String() - } -} - -func assertNotEqualf(t *testing.T, unexpected, actual interface{}, msg string, f ...interface{}) { - t.Helper() - if actual == unexpected { - diff := Diff([]byte(toStr(t, unexpected)), []byte(toStr(t, actual))) - t.Errorf("%[1]s\nUnexpected type %[2]T, actual type %[3]T\n%[4]s", fmt.Sprintf(msg, f...), unexpected, actual, diff) - } -} - -func assertEqual(t *testing.T, expected, actual interface{}) { - t.Helper() - assertEqualf(t, expected, actual, "Values are not equal.") -} - -func assertEqualf(t *testing.T, expected, actual interface{}, msg string, f ...interface{}) { - t.Helper() - if expected != actual { - diff := Diff([]byte(toStr(t, expected)), []byte(toStr(t, actual))) - t.Errorf("%[1]s\nExpected type %[2]T, actual type %[3]T\n%[4]s", fmt.Sprintf(msg, f...), expected, actual, diff) - } -} - -func assertErrf(t *testing.T, e error, msg string, f ...interface{}) { - t.Helper() - if e == nil { - if msg != "" { - msg = ": " + msg - } - t.Errorf("expected an error but got none"+msg, f...) - } -} - -func isNil(i interface{}) bool { - if i == nil { - return true - } - - switch reflect.TypeOf(i).Kind() { - case reflect.Ptr, reflect.Map, reflect.Array, reflect.Chan, reflect.Slice: - return reflect.ValueOf(i).IsNil() - } - return false -} - -func assertNotNilf(t *testing.T, obj interface{}, msg string, f ...interface{}) { - t.Helper() - if isNil(obj) { - if msg == "" { - t.Errorf("expected a value but got %v", obj) - return - } - - t.Errorf(msg, f...) - } -} - -func assertNilf(t *testing.T, obj interface{}, msg string, f ...interface{}) { - t.Helper() - if !isNil(obj) { - if msg == "" { - t.Errorf("expected nil but got %v", obj) - return - } - - t.Errorf(msg, f...) - } -} - -func assertNil(t *testing.T, e error) { - t.Helper() - assertNilf(t, e, "") -} - func TestAddTemplateFunctions(t *testing.T) { zulu.AddTemplateFunc("t", func() bool { return true }) zulu.AddTemplateFuncs(template.FuncMap{