348 lines
11 KiB
Bash
Executable File
348 lines
11 KiB
Bash
Executable File
#!/bin/sh
|
|
# POSIX smoke suite for the files agent tool.
|
|
# req: files/001 files/002 files/003 files/004 files/005 files/006 files/007 files/008 files/009 files/010 files/011 files/012 files/013 files/014
|
|
|
|
set -u
|
|
|
|
bin=${FILES_BIN:-./zig-out/bin/files}
|
|
|
|
fail() {
|
|
printf 'files-smoke: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_empty() {
|
|
file=$1
|
|
label=$2
|
|
[ ! -s "$file" ] || fail "$label not empty: $(cat "$file")"
|
|
}
|
|
|
|
assert_contains() {
|
|
file=$1
|
|
text=$2
|
|
label=$3
|
|
grep -F "$text" "$file" >/dev/null 2>&1 || fail "$label missing: $text"
|
|
}
|
|
|
|
assert_not_contains() {
|
|
file=$1
|
|
text=$2
|
|
label=$3
|
|
if grep -F "$text" "$file" >/dev/null 2>&1; then
|
|
fail "$label unexpectedly contains: $text"
|
|
fi
|
|
}
|
|
|
|
assert_status() {
|
|
got=$1
|
|
want=$2
|
|
label=$3
|
|
[ "$got" -eq "$want" ] || fail "$label exit $got, want $want"
|
|
}
|
|
|
|
assert_same_file() {
|
|
want=$1
|
|
got=$2
|
|
label=$3
|
|
cmp -s "$want" "$got" || fail "$label output mismatch"
|
|
}
|
|
|
|
assert_unchanged_tree() {
|
|
dir=$1
|
|
before=$2
|
|
label=$3
|
|
find "$dir" -print | sort > "$sorted"
|
|
assert_same_file "$before" "$sorted" "$label"
|
|
}
|
|
|
|
assert_runtime_failure() {
|
|
got=$1
|
|
label=$2
|
|
[ "$got" -ne 0 ] || fail "$label exit was zero"
|
|
[ "$got" -ne 2 ] || fail "$label used usage exit 2 for runtime failure"
|
|
}
|
|
|
|
assert_help_contract() {
|
|
file=$1
|
|
label=$2
|
|
assert_contains "$file" "usage: files [PATH...]" "$label"
|
|
assert_contains "$file" "stdout:" "$label"
|
|
assert_contains "$file" "One path per line" "$label"
|
|
assert_contains "$file" "exit status:" "$label"
|
|
assert_contains "$file" "paths only" "$label"
|
|
assert_contains "$file" "No filtering, sorting, metadata" "$label"
|
|
assert_contains "$file" "No deletion, exec, callbacks, network, hidden writes, config, or expression language" "$label"
|
|
}
|
|
|
|
write_tree_listing_expected() {
|
|
prefix=$1
|
|
{
|
|
printf '%s\n' "${prefix}--dash"
|
|
printf '%s\n' "${prefix}a/name with space"
|
|
printf '%s\n' "${prefix}a/one"
|
|
if [ "$have_symlink" = yes ]; then
|
|
printf '%s\n' "${prefix}linkdir"
|
|
printf '%s\n' "${prefix}linkfile"
|
|
fi
|
|
printf '%s\n' "${prefix}two"
|
|
} > "$expected"
|
|
}
|
|
|
|
[ -x "$bin" ] || fail "executable not found: $bin"
|
|
case $bin in
|
|
/*) abs_bin=$bin ;;
|
|
*) abs_bin=$(pwd -P)/$bin ;;
|
|
esac
|
|
|
|
i=0
|
|
base=${TMPDIR:-/tmp}/files-smoke.$$
|
|
while :; do
|
|
work=$base.$i
|
|
if mkdir "$work" 2>/dev/null; then
|
|
break
|
|
fi
|
|
i=$((i + 1))
|
|
[ "$i" -lt 50 ] || fail "could not create temp directory under ${TMPDIR:-/tmp}"
|
|
done
|
|
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
|
|
|
out=$work/out
|
|
err=$work/err
|
|
expected=$work/expected
|
|
sorted=$work/sorted
|
|
|
|
tree=$work/tree
|
|
mkdir -p "$tree/a" "$tree/empty" || fail "mkdir tree"
|
|
printf 'dash\n' > "$tree/--dash" || fail "write dash"
|
|
printf 'one\n' > "$tree/a/one" || fail "write one"
|
|
printf 'space\n' > "$tree/a/name with space" || fail "write space"
|
|
printf 'two\n' > "$tree/two" || fail "write two"
|
|
if ln -s a "$tree/linkdir" 2>/dev/null && ln -s two "$tree/linkfile" 2>/dev/null; then
|
|
have_symlink=yes
|
|
else
|
|
have_symlink=no
|
|
fi
|
|
|
|
"$bin" --help > "$out" 2> "$err"
|
|
assert_status $? 0 "--help"
|
|
assert_empty "$err" "--help stderr"
|
|
assert_help_contract "$out" "help"
|
|
|
|
"$bin" -h > "$out" 2> "$err"
|
|
assert_status $? 0 "-h"
|
|
assert_empty "$err" "-h stderr"
|
|
assert_help_contract "$out" "-h help"
|
|
|
|
help_roots=$work/help-roots
|
|
mkdir -p "$help_roots" || fail "mkdir help roots"
|
|
printf 'help\n' > "$help_roots/--help" || fail "write --help root"
|
|
printf 'short\n' > "$help_roots/-h" || fail "write -h root"
|
|
printf 'extra\n' > "$help_roots/extra" || fail "write extra root"
|
|
(
|
|
cd "$help_roots" || exit 1
|
|
"$abs_bin" --help extra > "$out" 2> "$err"
|
|
)
|
|
assert_status $? 0 "--help as path root"
|
|
assert_empty "$err" "--help as path root stderr"
|
|
{
|
|
printf '%s\n' "--help"
|
|
printf '%s\n' "extra"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "--help as path root"
|
|
(
|
|
cd "$help_roots" || exit 1
|
|
"$abs_bin" -h extra > "$out" 2> "$err"
|
|
)
|
|
assert_status $? 0 "-h as path root"
|
|
assert_empty "$err" "-h as path root stderr"
|
|
{
|
|
printf '%s\n' "-h"
|
|
printf '%s\n' "extra"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "-h as path root"
|
|
|
|
"$bin" "$tree" > "$out" 2> "$err"
|
|
assert_status $? 0 "directory listing"
|
|
assert_empty "$err" "directory listing stderr"
|
|
sort "$out" > "$sorted"
|
|
write_tree_listing_expected "$tree/"
|
|
sort "$expected" > "$expected.sorted"
|
|
mv "$expected.sorted" "$expected"
|
|
assert_same_file "$expected" "$sorted" "directory listing"
|
|
assert_not_contains "$out" "$tree/linkdir/one" "symlink traversal"
|
|
|
|
(
|
|
cd "$tree" || exit 1
|
|
"$abs_bin" . > "$out" 2> "$err"
|
|
)
|
|
assert_status $? 0 "default dot listing"
|
|
assert_empty "$err" "default dot stderr"
|
|
sort "$out" > "$sorted"
|
|
write_tree_listing_expected ""
|
|
sort "$expected" > "$expected.sorted"
|
|
mv "$expected.sorted" "$expected"
|
|
assert_same_file "$expected" "$sorted" "default dot"
|
|
|
|
(
|
|
cd "$tree" || exit 1
|
|
"$abs_bin" > "$out" 2> "$err"
|
|
)
|
|
assert_status $? 0 "no-arg default listing"
|
|
assert_empty "$err" "no-arg default stderr"
|
|
sort "$out" > "$sorted"
|
|
write_tree_listing_expected ""
|
|
sort "$expected" > "$expected.sorted"
|
|
mv "$expected.sorted" "$expected"
|
|
assert_same_file "$expected" "$sorted" "no-arg default"
|
|
|
|
"$bin" "$tree/empty" > "$out" 2> "$err"
|
|
assert_status $? 0 "empty directory"
|
|
assert_empty "$out" "empty directory stdout"
|
|
assert_empty "$err" "empty directory stderr"
|
|
|
|
"$bin" "$tree/two" > "$out" 2> "$err"
|
|
assert_status $? 0 "file input"
|
|
assert_empty "$err" "file input stderr"
|
|
printf '%s\n' "$tree/two" > "$expected"
|
|
assert_same_file "$expected" "$out" "file input"
|
|
|
|
single=$work/single
|
|
mkdir -p "$single" || fail "mkdir single"
|
|
printf 'single\n' > "$single/only" || fail "write single"
|
|
"$bin" "$tree/two" "$single" "$tree/--dash" > "$out" 2> "$err"
|
|
assert_status $? 0 "multiple roots"
|
|
assert_empty "$err" "multiple roots stderr"
|
|
{
|
|
printf '%s\n' "$tree/two"
|
|
printf '%s\n' "$single/only"
|
|
printf '%s\n' "$tree/--dash"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "multiple roots"
|
|
|
|
"$bin" "$tree/two" "$tree/two" "$single" "$single" > "$out" 2> "$err"
|
|
assert_status $? 0 "duplicate roots"
|
|
assert_empty "$err" "duplicate roots stderr"
|
|
{
|
|
printf '%s\n' "$tree/two"
|
|
printf '%s\n' "$tree/two"
|
|
printf '%s\n' "$single/only"
|
|
printf '%s\n' "$single/only"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "duplicate roots"
|
|
|
|
"$bin" "$single/" "$tree/two" > "$out" 2> "$err"
|
|
assert_status $? 0 "trailing slash root"
|
|
assert_empty "$err" "trailing slash root stderr"
|
|
{
|
|
printf '%s\n' "$single/only"
|
|
printf '%s\n' "$tree/two"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "trailing slash root"
|
|
assert_not_contains "$out" "$single//only" "trailing slash double slash"
|
|
|
|
if [ "$have_symlink" = yes ]; then
|
|
"$bin" "$tree/linkfile" "$tree/linkdir" > "$out" 2> "$err"
|
|
assert_status $? 0 "symlink roots"
|
|
assert_empty "$err" "symlink roots stderr"
|
|
{
|
|
printf '%s\n' "$tree/linkfile"
|
|
printf '%s\n' "$tree/linkdir"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "symlink roots"
|
|
assert_not_contains "$out" "$tree/linkdir/one" "symlink root traversal"
|
|
|
|
"$bin" "$tree/linkdir" "$single" > "$out" 2> "$err"
|
|
assert_status $? 0 "symlink then directory root"
|
|
assert_empty "$err" "symlink then directory root stderr"
|
|
{
|
|
printf '%s\n' "$tree/linkdir"
|
|
printf '%s\n' "$single/only"
|
|
} > "$expected"
|
|
assert_same_file "$expected" "$out" "symlink then directory root"
|
|
assert_not_contains "$out" "$tree/linkdir/one" "symlink then directory traversal"
|
|
fi
|
|
|
|
later_missing=$work/later-missing
|
|
if "$bin" "$tree/two" "$later_missing" > "$out" 2> "$err"; then
|
|
fail "missing later root unexpectedly succeeded"
|
|
else
|
|
status=$?
|
|
fi
|
|
assert_runtime_failure "$status" "missing later root"
|
|
printf '%s\n' "$tree/two" > "$expected"
|
|
assert_same_file "$expected" "$out" "missing later root partial stdout"
|
|
assert_contains "$err" "files: $later_missing:" "missing later root stderr"
|
|
|
|
if "$bin" "" > "$out" 2> "$err"; then
|
|
fail "empty path unexpectedly succeeded"
|
|
else
|
|
status=$?
|
|
fi
|
|
assert_runtime_failure "$status" "empty path"
|
|
assert_empty "$out" "empty path stdout"
|
|
assert_contains "$err" "files: empty PATH" "empty path stderr"
|
|
|
|
missing=$work/missing
|
|
if "$bin" "$missing" > "$out" 2> "$err"; then
|
|
fail "missing path unexpectedly succeeded"
|
|
else
|
|
status=$?
|
|
fi
|
|
assert_runtime_failure "$status" "missing path"
|
|
assert_empty "$out" "missing path stdout"
|
|
assert_contains "$err" "files: $missing:" "missing path stderr"
|
|
|
|
restricted=$work/restricted
|
|
mkdir -p "$restricted/blocked" || fail "mkdir restricted"
|
|
printf 'hidden\n' > "$restricted/blocked/file" || fail "write restricted file"
|
|
chmod 000 "$restricted/blocked" || fail "chmod restricted"
|
|
if "$bin" "$restricted" > "$out" 2> "$err"; then
|
|
status=0
|
|
else
|
|
status=$?
|
|
fi
|
|
chmod 700 "$restricted/blocked" || fail "restore restricted permissions"
|
|
if [ "$status" -eq 0 ]; then
|
|
if [ ! -r "$restricted/blocked" ] || [ ! -x "$restricted/blocked" ]; then
|
|
fail "unreadable directory unexpectedly succeeded"
|
|
fi
|
|
else
|
|
assert_runtime_failure "$status" "unreadable directory"
|
|
assert_empty "$out" "unreadable directory stdout"
|
|
assert_contains "$err" "files: $restricted:" "unreadable directory stderr"
|
|
fi
|
|
|
|
big=$work/big
|
|
mkdir -p "$big" || fail "mkdir big"
|
|
n=1
|
|
while [ "$n" -le 1000 ]; do
|
|
d=$big/d$((n % 10))
|
|
mkdir -p "$d" || fail "mkdir big dir $n"
|
|
: > "$d/$n" || fail "write big $n"
|
|
n=$((n + 1))
|
|
done
|
|
"$bin" "$big" > "$out" 2> "$err"
|
|
assert_status $? 0 "generated tree listing"
|
|
assert_empty "$err" "generated tree stderr"
|
|
count=$(wc -l < "$out" | tr -d ' ')
|
|
[ "$count" -eq 1000 ] || fail "generated tree count $count, want 1000"
|
|
{ "$bin" "$big" | head -n 1 > /dev/null; } 2> "$err"
|
|
assert_empty "$err" "pipeline stderr"
|
|
{ "$bin" "$big" "$work/pipe-missing" | head -n 1 > /dev/null; } 2> "$err"
|
|
assert_empty "$err" "multi-root pipeline stderr"
|
|
|
|
no_mutation=$work/no-mutation
|
|
mkdir -p "$no_mutation/home" "$no_mutation/cache" "$no_mutation/target/sub" || fail "mkdir no-mutation"
|
|
printf 'kept\n' > "$no_mutation/target/sub/file" || fail "write no-mutation file"
|
|
find "$no_mutation" -print | sort > "$expected"
|
|
HOME=$no_mutation/home XDG_CACHE_HOME=$no_mutation/cache "$bin" "$no_mutation/target" > "$out" 2> "$err"
|
|
assert_status $? 0 "no-mutation listing"
|
|
assert_empty "$err" "no-mutation listing stderr"
|
|
assert_unchanged_tree "$no_mutation" "$expected" "no-mutation listing"
|
|
HOME=$no_mutation/home XDG_CACHE_HOME=$no_mutation/cache "$bin" --help > "$out" 2> "$err"
|
|
assert_status $? 0 "no-mutation help"
|
|
assert_empty "$err" "no-mutation help stderr"
|
|
assert_unchanged_tree "$no_mutation" "$expected" "no-mutation help"
|
|
|
|
printf 'files-smoke: ok\n' >&2
|