单元测试Zsh自动补全脚本

3
我正在尝试编写Zsh的完成脚本。我想对完成脚本进行单元测试。例如,我想测试my-command --h的完成是否包括--help
对于Fish,我可以使用complete -C 'my-command --h',然后输出--help和任何其他有效的完成。
我似乎找不到Zsh的等效命令。有这样的命令吗?我尝试过像_main_complete_complete_normal这样的东西,但是它们要么不支持这个功能,要么我没有以正确的方式调用它们(我得到了很多can only be called from completion function错误)。
1个回答

4
我收到了很多“只能从完成函数调用”的错误。这是因为Zsh的完成命令只能在完成小部件内运行,而完成小部件只能在Zsh行编辑器处于活动状态时调用。我们可以通过在所谓的伪终端中激活一个活动命令行上的完成小部件来解决这个问题。
# 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

这在命令行中运行得很好,但是从脚本中,将compdef _my-command my-command放在comptest()内部,我没有得到任何结果。你知道是否可以自动化这个过程吗?理想情况下,我希望能够与ShellSpec一起使用。 - Nahoj
1
我的猜测是,当从非交互式会话中运行时,您需要将所有所需的代码都移动到comptest()中,包括对compinit的调用和my-command函数的定义。如果这样还不起作用,请提出一个新问题。 - Marlon Richert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接