Finch/tests/run.sh
Nexoniarz 636eaefe59 finch lsp: the language server, written in Finch
Hover, go to definition, completion (after a dot too: fields, methods, a module's names, an enum's variants,
array/text/map methods), signature help, the outline and errors while typing -- everything the C++ one did,
and the same tests/lsp_test.py passes against it.

It doesn't type-check by itself: for every change it runs  finch index  on the editor's text (--overlay says
which file the text stands in for) and reads what the compiler learned about every name, which is also what
the compiler's own error messages are made of. Half-typed code keeps using the last version that compiled.
src/docs.fch is the one description of the built-ins that hover and completion share.

tests/run.sh runs the LSP test too. 107 passed.
2026-10-25 21:48:11 +01:00

93 lines
3.4 KiB
Shell
Executable file

#!/usr/bin/env bash
# Finch test suite.
# tests/run/*.fch must compile and print exactly what is in the matching .out file
# (files without `fn main` are modules or helpers and are skipped;
# a matching .in file is fed to the program as its input)
# tests/fail/*.fch and tests/new/fail/*.fch must fail; the first line is "// expect: <text that must appear in the error>"
# the same programs built with -g -O0, and a library loaded by a C program (tests/shared.sh)
# MEMCHECK=1 tests/run.sh also runs every program under valgrind: no leaks, no bad memory access
set -u
cd "$(dirname "$0")"
FINCH=${FINCH:-../build/finch}
FINCH="$(cd "$(dirname "$FINCH")" && pwd)/$(basename "$FINCH")" # tests/shared.sh runs from another folder
export FINCH_RT=${FINCH_RT:-$PWD/../runtime/finch_rt.c}
pass=0; failed=0
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT
for f in run/*.fch new/*.fch; do
grep -q "^fn main" "$f" || continue
want="${f%.fch}.out"
input="${f%.fch}.in"
[ -f "$input" ] || input=/dev/null
got=$("$FINCH" run "$f" < "$input" 2>&1 | tr -d '\r') # Windows ends lines with \r\n
if [ "$got" != "$(tr -d '\r' < "$want")" ]; then
failed=$((failed + 1))
echo "FAIL $f"
diff <(echo "$got") "$want" | head -20
continue
fi
if [ "${MEMCHECK:-0}" = 1 ]; then
exe="$tmp/$(basename "${f%.fch}")"
"$FINCH" build "$f" -o "$exe" >/dev/null 2>&1
valgrind -q --leak-check=full --errors-for-leak-kinds=all --error-exitcode=99 "$exe" < "$input" >/dev/null 2>"$tmp/vg"
if [ $? -eq 99 ]; then # (other exit codes are the program's own)
failed=$((failed + 1))
echo "MEMCHECK FAIL $f"
head -20 "$tmp/vg"
continue
fi
fi
pass=$((pass + 1))
done
for f in fail/*.fch new/fail/*.fch; do
expect=$(head -1 "$f" | sed -n 's|^// expect: ||p')
got=$("$FINCH" run "$f" < /dev/null 2>&1)
status=$?
got=$(echo "$got" | tr -d '\r')
if [ $status -ne 0 ] && [[ "$got" == *"$expect"* ]]; then
pass=$((pass + 1))
else
failed=$((failed + 1))
echo "FAIL $f (wanted: $expect)"
echo "$got" | head -5
fi
done
# the same programs with debug info (-g -O0): the metadata has to be valid for every kind of thing a program has
for f in new/enums.fch new/generics.fch new/std_json.fch run/basics.fch run/methods_basic.fch run/maps.fch run/errors.fch \
run/defer.fch run/c_structs.fch run/memory.fch; do
input="${f%.fch}.in"
[ -f "$input" ] || input=/dev/null
if "$FINCH" build "$f" -g -O0 -o "$tmp/progg" > "$tmp/err" 2>&1 && [ "$("$tmp/progg" < "$input" 2>&1)" == "$(cat "${f%.fch}.out")" ]; then
pass=$((pass + 1))
else
failed=$((failed + 1))
echo "FAIL (-g) $f"
head -3 "$tmp/err"
fi
done
# a library a C program loads (finch build --shared)
if FINCH="$FINCH" ./shared.sh > "$tmp/shared.out" 2>&1; then
pass=$((pass + 1))
else
failed=$((failed + 1))
echo "FAIL shared.sh"
head -5 "$tmp/shared.out"
fi
# the language server, talked to the way an editor does
if command -v python3 > /dev/null 2>&1; then
if FINCH="$FINCH" python3 lsp_test.py > "$tmp/lsp.out" 2>&1; then
pass=$((pass + 1))
else
failed=$((failed + 1))
echo "FAIL lsp_test.py"
grep FAIL "$tmp/lsp.out" | head -5
fi
fi
echo "$pass passed, $failed failed"
[ $failed -eq 0 ]