1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
#!/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 '[flags]' 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
# run 'podman help', parse the output looking for 'Available Commands';
# return that list.
function podman_commands() {
dprint "$@"
run_podman help "$@" |\
awk '/^Available Commands:/{ok=1;next}/^Flags:/{ok=0}ok { print $1 }' |\
grep .
"$output"
}
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
# The line immediately after 'Usage:' gives us a 1-line synopsis
usage=$(echo "$output" | 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
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 '[FLAGS]' once. Prevent it.
if expr "$usage" : '.*\[FLAG' >/dev/null; then
die "'flags' string must be lower-case in usage: $usage"
fi
# We had someone do 'podman foo ARG [flags]' one time. Yeah, no.
if expr "$usage" : '.*[A-Z].*\[flag' >/dev/null; then
die "'flags' must precede arguments in usage: $usage"
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 125 "$@" $cmd invalid-arg
is "$output" "Error: .* takes no arguments" \
"'$command_string' with extra (invalid) arguments"
fi
found[takes_no_args]=1
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 [flags] 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 </dev/null protects us from 'podman login' which will
# try to read username/password from stdin.
dprint "$command_string (without required args)"
run_podman 125 "$@" $cmd </dev/null
is "$output" "Error:.* \(require\|specif\|must\|provide\|need\|choose\|accepts\)" \
"'$command_string' without required arg"
found[required_args]=1
fi
# Commands with fixed number of arguments (i.e. no ellipsis): count
# the required args, then invoke with one extra. We should get a
# usage error.
if ! expr "$usage" : ".*\.\.\."; then
# "podman help" can take infinite args, so skip that one
if [ "$cmd" != "help" ]; then
# Get the args part of the command line; this should be
# everything from the first CAPITAL LETTER onward. We
# don't actually care about the letter itself, so just
# make it 'X'. And we don't care about [OPTIONAL] brackets
# either. What we do care about is stuff like 'IMAGE | CTR'
# which is actually one argument; convert to 'IMAGE-or-CTR'
local rhs=$(sed -e 's/^[^A-Z]\+[A-Z]/X/' -e 's/ | /-or-/g' <<<"$usage")
local n_args=$(wc -w <<<"$rhs")
run_podman 125 "$@" $cmd $(seq --format='x%g' 0 $n_args)
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, must throw error if called
# without one.
dprint "podman $@"
run_podman 125 "$@"
is "$output" "Error: missing command .*$@ COMMAND"
# Assume that 'NoSuchCommand' is not a command
dprint "podman $@ NoSuchCommand"
run_podman 125 "$@" NoSuchCommand
is "$output" "Error: unrecognized command .*$@ NoSuchCommand"
# 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
}
# vim: filetype=sh
|