#!/usr/bin/env bats # # Tests based on 'podman help' # # Find all commands listed by 'podman --help'. Run each one, make sure it # provides its own --help output. If the usage message ends in '[command]', # treat it as a subcommand, and recurse into its own list of sub-subcommands. # # Any usage message that ends in '[options]' is interpreted as a command # that takes no further arguments; we confirm by running with 'invalid-arg' # and confirming that it exits with error status and message. # load helpers function check_help() { local count=0 local -A found for cmd in $(_podman_commands "$@"); do # Human-readable podman command string, with multiple spaces collapsed command_string="podman $* $cmd" command_string=${command_string// / } # 'podman x' -> 'podman x' dprint "$command_string --help" run_podman "$@" $cmd --help local full_help="$output" # The line immediately after 'Usage:' gives us a 1-line synopsis usage=$(echo "$full_help" | grep -A1 '^Usage:' | tail -1) [ -n "$usage" ] || die "podman $cmd: no Usage message found" # e.g. 'podman ps' should not show 'podman container ps' in usage # Trailing space in usage handles 'podman system renumber' which # has no ' [options]' is "$usage " " $command_string .*" "Usage string matches command" # If usage ends in '[command]', recurse into subcommands if expr "$usage" : '.*\[command\]$' >/dev/null; then found[subcommands]=1 check_help "$@" $cmd continue fi # We had someone write upper-case '[OPTIONS]' once. Prevent it. if expr "$usage" : '.*\[OPTION' >/dev/null; then die "'options' string must be lower-case in usage: $usage" fi # We had someone do 'podman foo ARG [options]' one time. Yeah, no. if expr "$usage" : '.*[A-Z].*\[option' >/dev/null; then die "'options' must precede arguments in usage: $usage" fi # Cross-check: if usage includes '[options]', there must be a # longer 'Options:' section in the full --help output; vice-versa, # if 'Options:' is in full output, usage line must have '[options]'. if expr "$usage" : '.*\[option' >/dev/null; then if ! expr "$full_help" : ".*Options:" >/dev/null; then die "$command_string: Usage includes '[options]' but has no 'Options:' subsection" fi elif expr "$full_help" : ".*Options:" >/dev/null; then die "$command_string: --help has 'Options:' section but no '[options]' in synopsis" fi # If usage lists no arguments (strings in ALL CAPS), confirm # by running with 'invalid-arg' and expecting failure. if ! expr "$usage" : '.*[A-Z]' >/dev/null; then if [ "$cmd" != "help" ]; then dprint "$command_string invalid-arg" run_podman '?' "$@" $cmd invalid-arg is "$status" 125 "'$command_string invalid-arg' - exit status" is "$output" "Error: .* takes no arguments" \ "'$command_string' with extra (invalid) arguments" fi found[takes_no_args]=1 fi # If command lists "-l, --latest" in help output, combine -l with arg. # This should be disallowed with a clear message. if expr "$full_help" : ".*-l, --latest" >/dev/null; then local nope="exec list port ps top" # these can't be tested if is_rootless; then nope="$nope mount restore" # these don't work rootless fi if ! grep -wq "$cmd" <<<$nope; then run_podman 125 "$@" $cmd -l nonexistent-container is "$output" "Error: .*--latest and \(containers\|pods\|arguments\) cannot be used together" \ "'$command_string' with both -l and container" # Combine -l and -a, too (but spell it as --all, because "-a" # means "attach" in podman container start) run_podman 125 "$@" $cmd --all --latest is "$output" "Error: \(--all and --latest cannot be used together\|--all, --latest and containers cannot be used together\|--all, --latest and arguments cannot be used together\|unknown flag\)" \ "'$command_string' with both --all and --latest" fi fi # If usage has required arguments, try running without them. # The expression here is 'first capital letter is not in [BRACKETS]'. # It is intended to handle 'podman foo [options] ARG' but not ' [ARG]'. if expr "$usage" : '[^A-Z]\+ [A-Z]' >/dev/null; then # Exceptions: these commands don't work rootless if is_rootless; then # "pause is not supported for rootless containers" if [ "$cmd" = "pause" -o "$cmd" = "unpause" ]; then continue fi # "network rm" too if [ "$@" = "network" -a "$cmd" = "rm" ]; then continue fi fi # The $n_args arguments - exit status" is "$output" "Error:.* \(takes no arguments\|requires exactly $n_args arg\|accepts at most\|too many arguments\|accepts $n_args arg(s), received\|accepts between .* and .* arg(s), received \)" \ "'$command_string' with >$n_args arguments" found[fixed_args]=1 fi fi count=$(expr $count + 1) done # Any command that takes subcommands, prints its help and errors if called # without one. dprint "podman $@" run_podman '?' "$@" is "$status" 125 "'podman $*' without any subcommand - exit status" is "$output" ".*Usage:.*Error: missing command '.*$@ COMMAND'" \ "'podman $*' without any subcommand - expected error message" # Assume that 'NoSuchCommand' is not a command dprint "podman $@ NoSuchCommand" run_podman '?' "$@" NoSuchCommand is "$status" 125 "'podman $* NoSuchCommand' - exit status" is "$output" "Error: unrecognized command .*$@ NoSuchCommand" \ "'podman $* NoSuchCommand' - expected error message" # This can happen if the output of --help changes, such as between # the old command parser and cobra. [ $count -gt 0 ] || \ die "Internal error: no commands found in 'podman help $@' list" # Sanity check: make sure the special loops above triggered at least once. # (We've had situations where a typo makes the conditional never run) if [ -z "$*" ]; then for i in subcommands required_args takes_no_args fixed_args; do if [[ -z ${found[$i]} ]]; then die "Internal error: '$i' subtest did not trigger" fi done fi } @test "podman help - basic tests" { skip_if_remote # Called with no args -- start with 'podman --help'. check_help() will # recurse for any subcommands. check_help # Test for regression of #7273 (spurious "--remote" help on output) for helpopt in help --help; do run_podman $helpopt is "${lines[0]}" "Manage pods, containers and images" \ "podman $helpopt: first line of output" done } # vim: filetype=sh