单元测试 Zsh 完成脚本

Unit testing Zsh completion script

我正在尝试为 Zsh 编写完成脚本。我想对完成脚本进行单元测试。例如,我想测试 my-command --h 的完成包括 --help.

对于 Fish,我可以使用 complete -C 'my-command --h',然后它会输出 --help 和任何其他有效的补全。

我似乎找不到 Zsh 的等效命令。一个存在吗?我已经尝试过 _main_complete_complete_normal 之类的东西,但要么他们不支持这个,要么我没有以正确的方式调用它们(我得到了很多 can only be called from completion function 错误)。

I get a lot of can only be called from completion function errors

这是因为 Zsh 的完成命令 运行 只能来自完成小部件内部,反过来只能在 Zsh 行编辑器处于活动状态时调用。我们可以通过在所谓的 pseudo terminal:

中的活动命令行上激活完成小部件来解决这个问题
# Set up your completions as you would normally.
compdef _my-command my-command
_my-command () {
        _arguments '--help[display help text]'  # Just an example.
}

# Define our test function.
comptest () {
        # Gather all matching completions in this array.
        # -U discards duplicates.
        typeset -aU completions=()  

        # Override the builtin compadd command.
        compadd () {
                # Gather all matching completions for this call in $reply.
                # Note that this call overwrites the specified array.
                # Therefore we cannot use $completions directly.
                builtin compadd -O reply "$@"

                completions+=("$reply[@]") # Collect them.
                builtin compadd "$@"       # Run the actual command.
        }

        # Bind a custom widget to TAB.
        bindkey "^I" complete-word
        zle -C {,,}complete-word
        complete-word () {
                # Make the completion system believe we're on a normal 
                # command line, not in vared.
                unset 'compstate[vared]'

                _main_complete "$@"  # Generate completions.

                # Print out our completions.
                # Use of ^B and ^C as delimiters here is arbitrary.
                # Just use something that won't normally be printed.
                print -n $'\C-B'
                print -nlr -- "$completions[@]"  # Print one per line.
                print -n $'\C-C'
                exit
        }

        vared -c tmp
}

zmodload zsh/zpty  # Load the pseudo terminal module.
zpty {,}comptest   # Create a new pty and run our function in it.

# Simulate a command being typed, ending with TAB to get completions.
zpty -w comptest $'my-command --h\t'

# Read up to the first delimiter. Discard all of this.
zpty -r comptest REPLY $'*\C-B'

zpty -r comptest REPLY $'*\C-C'  # Read up to the second delimiter.

# Print out the results.
print -r -- "${REPLY%$'\C-C'}"   # Trim off the ^C, just in case.

zpty -d comptest  # Delete the pty.

运行上面的例子会打印出:

--help

如果您想测试整个完成输出而不仅仅是要在命令行中插入的字符串,请参阅 https://unix.stackexchange.com/questions/668618/how-to-write-automated-tests-for-zsh-completion/668827#668827