Files
gw-basic-2026/tests/run_compat.sh
Eremey Valetov ad21350003 Add full-screen TUI editor, DOSBox-X compat testing, rename to GW-BASIC 2026
Authentic GW-BASIC screen editor with 25x80 buffer, free cursor movement,
enter-on-any-line, F1-F10 function keys, Insert/Overwrite toggle, KEY
ON/OFF/LIST statement, and Ctrl+Break handling. HAL pointer swap routes
all PRINT/LIST/error output through the TUI automatically. Piped mode
unchanged (50/50 tests pass).

Adds automated compatibility testing infrastructure: DOSBox-X headless
config, PRINT-to-file transform script, and run_compat.sh with --generate
and --compare modes for verifying output against real GWBASIC.EXE.

Project renamed from gwbasic-c to GW-BASIC 2026.
2026-02-22 12:18:17 -05:00

188 lines
5.1 KiB
Bash
Executable File

#!/bin/bash
# Compatibility test runner: compare gwbasic output against real GWBASIC.EXE
#
# Usage:
# tests/run_compat.sh --generate Generate .expected files from GWBASIC.EXE via DOSBox-X
# tests/run_compat.sh Compare gwbasic output against .expected files
set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
GWBASIC="${PROJECT_DIR}/build/gwbasic"
EXPECTED_DIR="${SCRIPT_DIR}/expected"
TRANSFORM="${SCRIPT_DIR}/transform_for_capture.py"
DOSBOX_FLATPAK="com.dosbox_x.DOSBox-X"
DOSBOX_CONF="${SCRIPT_DIR}/dosbox-compat.conf"
GWBASIC_EXE="/home/evaletov/DOS/dosbox-x/C_DRIVE/GWBASIC/GWBASIC.EXE"
C_DRIVE="/home/evaletov/DOS/dosbox-x/C_DRIVE"
# Tests to skip (file I/O with host paths, interactive, sound, graphics, CHAIN)
SKIP_LIST=(
chain_test.bas
chain_target.bas
file_io.bas
random_access.bas
save_load.bas
run_file.bas
text_adventure.bas
number_guess.bas
graphics_stubs.bas
sound_test.bas
play_music.bas
play_scale.bas
write_input.bas
mkicvi.bas
invoice.bas
)
should_skip() {
local name="$1"
for skip in "${SKIP_LIST[@]}"; do
[ "$name" = "$skip" ] && return 0
done
return 1
}
normalize_output() {
# Normalize line endings and trailing whitespace for comparison
sed 's/\r//g; s/[[:space:]]*$//' "$1" | sed '/^$/d'
}
generate_expected() {
echo "=== Generating expected output from GWBASIC.EXE via DOSBox-X ==="
mkdir -p "$EXPECTED_DIR"
local tmpdir
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
local count=0
local errors=0
for bas in "$SCRIPT_DIR"/programs/*.bas; do
local name
name="$(basename "$bas")"
should_skip "$name" && continue
local stem="${name%.bas}"
local dos_bas="$tmpdir/${stem}.BAS"
local dos_out="OUTPUT.TXT"
# Transform the program for file-based output capture
python3 "$TRANSFORM" "$bas" "$dos_bas" "$dos_out"
# Copy to C drive
cp "$dos_bas" "$C_DRIVE/${stem}.BAS"
# Create autoexec batch to run the program
local bat="$C_DRIVE/RUNTEST.BAT"
printf '@ECHO OFF\r\nC:\\GWBASIC\\GWBASIC.EXE /F:9 C:\\%s.BAS\r\nEXIT\r\n' "$stem" > "$bat"
# Remove any previous output
rm -f "$C_DRIVE/$dos_out"
# Run DOSBox-X headless
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
flatpak run "$DOSBOX_FLATPAK" \
-conf "$DOSBOX_CONF" \
-c "MOUNT C $C_DRIVE" \
-c "C:" \
-c "CALL RUNTEST.BAT" \
-c "EXIT" \
>/dev/null 2>&1 &
local pid=$!
# Wait with timeout
local timeout=15
local elapsed=0
while kill -0 "$pid" 2>/dev/null && [ "$elapsed" -lt "$timeout" ]; do
sleep 1
elapsed=$((elapsed + 1))
done
if kill -0 "$pid" 2>/dev/null; then
kill "$pid" 2>/dev/null
wait "$pid" 2>/dev/null
printf " TIMEOUT %s\n" "$name"
errors=$((errors + 1))
continue
fi
wait "$pid" 2>/dev/null
# Copy output
if [ -f "$C_DRIVE/$dos_out" ]; then
normalize_output "$C_DRIVE/$dos_out" > "$EXPECTED_DIR/${stem}.expected"
printf " OK %s\n" "$name"
count=$((count + 1))
else
printf " NO-OUT %s\n" "$name"
errors=$((errors + 1))
fi
# Cleanup
rm -f "$C_DRIVE/${stem}.BAS" "$C_DRIVE/$dos_out" "$bat"
done
echo ""
echo "Generated $count expected files ($errors errors) in $EXPECTED_DIR"
}
compare_output() {
if [ ! -x "$GWBASIC" ]; then
echo "ERROR: gwbasic not found at $GWBASIC (run cmake/make first)"
exit 1
fi
echo "=== Compatibility test: gwbasic vs .expected ==="
local pass=0
local fail=0
local skip=0
local missing=0
for bas in "$SCRIPT_DIR"/programs/*.bas; do
local name
name="$(basename "$bas")"
should_skip "$name" && { skip=$((skip + 1)); continue; }
local stem="${name%.bas}"
local expected="$EXPECTED_DIR/${stem}.expected"
if [ ! -f "$expected" ]; then
printf " SKIP %s (no .expected file)\n" "$name"
missing=$((missing + 1))
continue
fi
local actual
actual=$(mktemp)
if timeout 5 "$GWBASIC" "$bas" 2>/dev/null | sed 's/\r//g; s/[[:space:]]*$//' | sed '/^$/d' > "$actual"; then
if diff -q "$expected" "$actual" >/dev/null 2>&1; then
printf " PASS %s\n" "$name"
pass=$((pass + 1))
else
printf " FAIL %s\n" "$name"
diff -u "$expected" "$actual" | head -20
fail=$((fail + 1))
fi
else
printf " ERROR %s\n" "$name"
fail=$((fail + 1))
fi
rm -f "$actual"
done
echo ""
echo "$((pass + fail)) compared: $pass passed, $fail failed ($skip skipped, $missing no expected)"
[ "$fail" -eq 0 ] || exit 1
}
case "${1:-}" in
--generate)
generate_expected
;;
*)
compare_output
;;
esac