llm
Download
131 lines 4.6 KiBBashModified 2026-09-28executable raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
#!/bin/sh
# End-to-end tests for the command-line interfaces Tomo generates. A program's
# argument parsing, `--help`, and manpage only exist once it is compiled, so
# these build the fixtures in test/cli and drive the real programs.
#
# Usage: cli_tests.sh TOMO
set -e

tomo=$1
if [ -z "$tomo" ]; then
    echo "usage: $0 TOMO" >&2
    exit 1
fi

# Help text is colorized only for a terminal, which this isn't, but a
# developer's COLOR=1 would bake escapes into the output:
COLOR=0
LC_ALL=C
export COLOR LC_ALL

repo="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P)"
# The fixtures are built from their own directory, so the compiler needs an
# absolute path:
case $tomo in
    /*) ;;
    *) tomo="$(CDPATH= cd -- "$(dirname -- "$tomo")" && pwd -P)/$(basename -- "$tomo")" ;;
esac
cd "$repo/test/cli"

checked=0
failed=0

report() { # report DESCRIPTION EXPECTED ACTUAL
    checked=$((checked + 1))
    if [ "$3" = "$2" ]; then
        printf '  \033[32mok\033[m %s\n' "$1"
    else
        failed=$((failed + 1))
        printf '  \033[31mFAIL\033[m %s\n' "$1"
        printf '    expected: %s\n' "$(printf '%s' "$2" | tr '\n' '|')"
        printf '      actual: %s\n' "$(printf '%s' "$3" | tr '\n' '|')"
    fi
}

# run PROGRAM.tm ARGS... -- the program's output, with its arguments after `--`
# so they go to the program instead of to `tomo`
run() {
    program=$1
    shift
    "$tomo" "$program" -- "$@" 2>&1 || true
}

# check DESCRIPTION EXPECTED_OUTPUT PROGRAM.tm ARGS...
check() {
    description=$1
    expected=$2
    shift 2
    report "$description" "$expected" "$(run "$@")"
}

# check_contains DESCRIPTION SUBSTRING PROGRAM.tm ARGS...
check_contains() {
    description=$1
    substring=$2
    shift 2
    checked=$((checked + 1))
    if run "$@" | grep -qF "$substring"; then
        printf '  \033[32mok\033[m %s\n' "$description"
    else
        failed=$((failed + 1))
        printf '  \033[31mFAIL\033[m %s\n' "$description"
        printf '    expected output containing: %s\n' "$substring"
    fi
}

# check_grep DESCRIPTION PATTERN PROGRAM.tm ARGS... -- the matching lines
check_grep() {
    description=$1
    pattern=$2
    shift 2
    report "$description" "$pattern" "$(run "$@" | grep -F "$pattern" | head -n 1 | sed 's/^ *//;s/ *$//')"
}

# A `use`d file's subcommand is dispatchable, with its own flags, defaults, and
# doc comment, exactly like one the program defines itself:
check 'a `use`d file'"'"'s subcommand runs' 'add ["a.txt", "b.txt"] force=yes' \
    tool.tm add --files a.txt b.txt --force
check 'its defaults apply' 'fetch origin' tool.tm fetch
check 'its flags are parsed' 'fetch upstream' tool.tm fetch --remote=upstream
check 'a nested imported subcommand runs' 'plugin init ["vendor/foo"]' \
    tool.tm plugin init --paths vendor/foo
check 'the program'"'"'s own subcommand runs' 'add ["everything"] force=no
commit hello' tool.tm commit -m hello
check 'a plain main() still runs with no subcommand' 'status short=yes' tool.tm -s

# The root listing covers both files' commands, each with its summary:
check_grep 'imported subcommands are listed in --help' \
    'add     Add file contents to the index' tool.tm --help
check_grep 'so are their sub-subcommands' 'plugin  <command> ...' tool.tm --help
check_grep 'and the program'"'"'s own' 'commit  Record changes to the repository' \
    tool.tm --help
check_contains 'an imported subcommand has its own --help' \
    'add --files text1 text2... [--force|-f]' tool.tm add --help

# Importing a module under a name keeps its subcommands out of the program's
# interface (they stay reachable as `commands.main.add(...)`):
check 'a named import contributes no subcommands' 'add ["direct"] force=yes' named_import.tm
report 'a named import'"'"'s commands are not in --help' '' \
    "$(run named_import.tm --help | grep -F 'Commands:' || true)"

# Every subcommand is documented in the manpage, wherever it was defined. This
# installs into a scratch prefix, since an installed man/man1/ is where a
# manpage is meant to be read from:
prefix=$(mktemp -d)
trap 'rm -rf "$prefix"' EXIT HUP INT TERM
"$tomo" build tool.tm --install --prefix "$prefix" --yes -o "$prefix/tool" >/dev/null 2>&1
for command in 'tool add' 'tool plugin init' 'tool commit'; do
    checked=$((checked + 1))
    if grep -qF "$command" "$prefix/man/man1/tool.1"; then
        printf '  \033[32mok\033[m manpage documents `%s`\n' "$command"
    else
        failed=$((failed + 1))
        printf '  \033[31mFAIL\033[m manpage is missing `%s`\n' "$command"
    fi
done

if [ "$failed" -gt 0 ]; then
    printf '\033[31;1m%d of %d CLI tests failed\033[m\n' "$failed" "$checked"
    exit 1
fi
printf 'All %d CLI tests passed.\n' "$checked"