#!/bin/sh
# Verify command-line parsing and validation through the compiler driver.
set -eu
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT HUP INT TERM
checks=0

# Use the compiler's standard Make memory and stack settings.
command() {
    timeout 30 "${MAKE:-make}" --no-print-directory driver-command \
        RAD_BIN="${RAD_BIN:-bin/radiance.rv64.dev}" \
        RAD_EMULATOR="${RAD_EMULATOR:-emulator}" \
        DRIVER_ARGUMENTS="$1" > "$work/command.log" 2>&1
}

# Require a specific diagnostic, including checks that precede file loading.
reject() {
    if command "$1"; then
        echo "command: unexpected success: $1"
        exit 1
    fi
    if ! grep -Fq -- "$2" "$work/command.log"; then
        cat "$work/command.log"
        echo "command: missing diagnostic: $2"
        exit 1
    fi
    checks=$((checks + 1))
}

# Require successful compilation or dumping.
accept() {
    if ! command "$1"; then
        cat "$work/command.log"
        exit 1
    fi
    checks=$((checks + 1))
}

while IFS='|' read -r arguments expected; do
    reject "$arguments" "$expected"
done <<'CASES'
|usage: radiance
-pkg|`-pkg` requires a package name
-mod|`-mod` requires a module path
-start|`-start` requires an assembly path
-entry|`-entry` requires a package name
-o|`-o` requires an output path
-ril|`-ril` requires an output directory
-dump|`-dump` requires a mode
-mod absent.rad|`-mod` must follow a `-pkg` argument
-start absent.ras|`-start` must follow a `-pkg` argument
-dump invalid|unknown dump mode invalid
-unknown|unknown argument -unknown
-test|no package specified
-pkg p|package p has no Radiance modules specified
-pkg p -mod absent.ras|package p has no Radiance modules specified
-pkg p -mod p.rad -start x.rad|`-start` requires a `.ras` assembly file
-pkg p -mod p.rad -start x.ras -start y.ras|has more than one startup file
-pkg p -mod p.rad -pkg q -mod q.rad|`-entry` required when multiple packages specified
-pkg p -mod p.rad -pkg q -mod q.rad -entry missing|entry package missing not found
-pkg p -mod p.rad -start x.ras -pkg q -mod q.rad -entry q|`-start` is only supported on the entry package
-pkg p -mod p.rad -ril out -o out.rv64|`-ril` requires a separate invocation
-pkg p -mod p.rad -ril out -dump ast|`-ril` requires a separate invocation
CASES

# At each path limit, parsing reaches the deliberately absent source file.
arguments='-pkg p'
for count in $(seq 1 129); do
    arguments="$arguments -mod $work/absent.rad"
    if [ "$count" -eq 128 ]; then reject "$arguments" 'error reading file'; fi
done
reject "$arguments" 'too many modules specified for package'
arguments="-pkg p -mod $work/absent.rad"
for count in $(seq 1 65); do
    arguments="$arguments -mod $work/absent.ras"
    if [ "$count" -eq 64 ]; then reject "$arguments" 'error reading file'; fi
done
reject "$arguments" 'too many assembly modules specified'
reject '-pkg a -pkg b -pkg c -pkg d -pkg e' 'too many packages specified'

printf '%s\n' '/// Executable command fixture.' '@default fn main() -> u32 { return 0; }' > "$work/p.rad"
for mode in ast graph il asm; do
    accept "-pkg p -mod $work/p.rad -dump $mode"
done
# A single package selects itself; the final output option selects the path.
accept "-pkg p -mod $work/p.rad -entry unused -debug -o $work/first.rv64 -o $work/final.rv64"
test ! -e "$work/first.rv64"
test -s "$work/final.rv64"
timeout 10 "${RAD_EMULATOR:-emulator}" -run "$work/final.rv64"

arguments=''
for name in a b c; do
    printf '%s\n' '/// Package fixture value.' 'constant value: u32 = 0;' > "$work/$name.rad"
    arguments="$arguments -pkg $name -mod $work/$name.rad"
done
accept "$arguments -pkg p -mod $work/p.rad -entry p -o $work/multiple.rv64"
timeout 10 "${RAD_EMULATOR:-emulator}" -run "$work/multiple.rv64"

# Binary package output preserves bytes across destinations and replacements.
mkdir "$work/packages"
accept "$arguments -pkg p -mod $work/p.rad -entry p -ril $work/packages"
for name in a b c p; do
    test -s "$work/packages/$name.ril"
done
cp "$work/packages/p.ril" "$work/expected.ril"
printf '%s\n' 'stale output bytes' >> "$work/packages/p.ril"
accept "-pkg p -mod $work/p.rad -ril $work/packages"
cmp "$work/expected.ril" "$work/packages/p.ril"

# MAX_PATH_LEN includes the terminating zero byte.
for length in 249 250 255 256; do
    leaf=$(awk -v n="$((length - ${#work} - 1))" 'BEGIN { for (i = 0; i < n; i++) printf "x" }')
    directory="$work/$leaf"
    if [ "$length" -eq 249 ]; then
        mkdir "$directory"
        accept "-pkg p -mod $work/p.rad -ril $directory"
        cmp "$work/expected.ril" "$directory/p.ril"
        reject "-pkg package -mod $work/p.rad -ril $directory" 'binary RIL output path is too long'
    else
        reject "-pkg p -mod $work/p.rad -ril $directory" 'binary RIL output path is too long'
    fi
done
reject "-pkg p -mod $work/p.rad -ril $work/missing" 'cannot write binary RIL package p'
mkdir -p "$work/blocked/p.ril"
reject "-pkg p -mod $work/p.rad -ril $work/blocked" 'cannot write binary RIL package p'
reject "-pkg p -mod $work/p.rad -mod absent.ras -ril $work/packages" 'binary RIL output requires Radiance source modules'
reject "-pkg p -mod $work/p.rad -start absent.ras -ril $work/packages" 'binary RIL output requires Radiance source modules'
echo "command tests: $checks passed"
