diff --git a/.gitignore b/.gitignore index d479896..d651f66 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,7 @@ android/build/ # Android's "don't index this as media" marker, not project content. .nomedia + +# Per-test verdicts from the last tests/run_tests.sh, kept only so a long run +# whose terminal output was lost can still be read back. +.test-results/ diff --git a/tests/_run_inside.sh b/tests/_run_inside.sh index a63c666..69f51e0 100644 --- a/tests/_run_inside.sh +++ b/tests/_run_inside.sh @@ -1,15 +1,19 @@ #!/bin/bash -# Runs INSIDE the proot-distro Ubuntu container - do not call this directly, -# call tests/run_tests.sh from Termux instead. +# The half of the runner that actually runs Godot - do not call this +# directly, call tests/run_tests.sh instead. # -# It exists as its own file so the whole suite runs in ONE `proot-distro -# login`: entering the container costs ~0.5s, which used to be paid 38 times -# over. Everything below is the container-side half of the runner. +# On Termux it runs INSIDE the proot-distro Ubuntu container. It exists as +# its own file so the whole suite runs in ONE `proot-distro login`: entering +# the container costs ~0.5s, which used to be paid 38 times over. On WSL +# there is no container to enter and run_tests.sh calls it directly - the +# scheduling, timeouts and verdicts below are the same either way, which is +# why the Godot binary arrives as an argument rather than being hardcoded. # -# Args: ... +# Args: ... set -uo pipefail +GODOT_BIN="$1"; shift PROJECT_DIR="$1"; shift JOBS="$1"; shift VERBOSE="$1"; shift @@ -19,7 +23,6 @@ VERBOSE="$1"; shift # rather than making the suite look hung. Override for deliberate long runs. TEST_TIMEOUT_SECONDS="${TEST_TIMEOUT_SECONDS:-120}" -GODOT_BIN="/root/Godot_v4.7.1-stable_linux.arm64" cd "$PROJECT_DIR" || exit 2 tests=("$@") diff --git a/tests/run_tests.sh b/tests/run_tests.sh index eea839a..3b949ec 100644 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -1,15 +1,27 @@ -#!/data/data/com.termux/files/usr/bin/bash -# Runs the headless GDScript gameplay tests from Termux, via the Godot -# 4.7.1 Linux arm64 build installed inside the proot-distro Ubuntu container. +#!/usr/bin/env bash +# Runs the headless GDScript gameplay tests. # # bash tests/run_tests.sh # everything # bash tests/run_tests.sh formation # only tests matching a substring # JOBS=1 bash tests/run_tests.sh # serial, for debugging a hang # VERBOSE=1 bash tests/run_tests.sh # print passing tests' output too # IN_PLACE=1 bash tests/run_tests.sh # run from the project dir itself +# GODOT_BIN=/path/to/godot bash tests/run_tests.sh # pick the binary # -# Three things make this fast, in descending order of how much they matter -# (measured, not guessed - see tests/_profile_startup.gd): +# There are two ways to get a Godot to run them with, picked automatically: +# +# Termux the Godot 4.7 Linux arm64 build installed inside the +# proot-distro Ubuntu container, which is the only way to run +# a desktop Godot on the phone at all. +# WSL the Windows Godot build, run straight from /mnt/c. A Linux +# `godot` on PATH is used first if there is one. +# +# The platform decides which Godot runs and whether the project is copied +# first; the worker count and tests/_run_inside.sh, which does the actual +# running, are shared. +# +# On Termux, three things make this fast, in descending order of how much +# they matter (measured, not guessed - see tests/_profile_startup.gd): # # 1. It runs from a COPY on Termux's own filesystem, not from the project # directory. The project lives under ~/storage/shared, which is Android's @@ -17,7 +29,9 @@ # alone took 8.5s per test, against ~2s from a native path. Nearly every # test loads Main.tscn, and none of that time is gameplay. The copy is # ~1.4s for the whole suite and its .godot import cache is kept between -# runs. +# runs. WSL skips the copy: a Windows Godot reads C: natively, and the +# only thing copying to ext4 would achieve is putting the project behind +# the \\wsl$ share instead. # 2. Tests run JOBS at a time. Each is its own Godot process - they call # quit() and stamp on the GameState autoload, so they cannot share one - # which makes the suite a pile of independent, single-core-bound jobs on @@ -32,6 +46,32 @@ set -uo pipefail PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PLATFORM="termux" +if [[ -n "${WSL_DISTRO_NAME:-}" ]] || grep -qi microsoft /proc/version 2>/dev/null; then + PLATFORM="wsl" +fi + +# Finding a Godot on WSL. A Linux build on PATH wins - no interop, no drive +# letters - but there usually is not one, and the Windows build the editor +# already runs from is right there on /mnt/c. Two things about picking that +# one up: +# - it must be the *_console.exe. The plain .exe is built as a Windows GUI +# subsystem binary: it detaches from the terminal, so every test would +# produce an empty log and the runner would call all of them failures. +# - sort -V so 4.7.2 beats 4.6.1 when several versions are downloaded. +_find_godot_wsl() { + local found + found="$(command -v godot 2>/dev/null)" + if [[ -n "$found" ]]; then + echo "$found" + return + fi + found="$(find /mnt/c/Users/*/Downloads /mnt/c/Users/*/AppData/Local/Programs \ + "/mnt/c/Program Files" -maxdepth 2 -name 'Godot_v*_console.exe' \ + 2>/dev/null | sort -V | tail -1)" + echo "$found" +} + # Worker count comes from FREE MEMORY, not core count. Each Godot instance is # ~400MB and this phone shares its RAM with everything else running on it. # Getting it wrong is bad in both directions: @@ -63,9 +103,28 @@ _pick_jobs() { } JOBS="${JOBS:-$(_pick_jobs)}" VERBOSE="${VERBOSE:-0}" -IN_PLACE="${IN_PLACE:-0}" +# The copy only buys anything on Termux (see 1. above), so on WSL the project +# directory IS the run directory whatever IN_PLACE says. +if [[ "$PLATFORM" == "wsl" ]]; then + IN_PLACE=1 +else + IN_PLACE="${IN_PLACE:-0}" +fi WORK_DIR="${WORK_DIR:-$HOME/.cache/spaceship-tests}" +if [[ -z "${GODOT_BIN:-}" ]]; then + if [[ "$PLATFORM" == "wsl" ]]; then + GODOT_BIN="$(_find_godot_wsl)" + if [[ -z "$GODOT_BIN" ]]; then + echo "no Godot found. Install one on PATH, or download a Windows" >&2 + echo "build and point GODOT_BIN at its *_console.exe." >&2 + exit 2 + fi + else + GODOT_BIN="/root/Godot_v4.7.1-stable_linux.arm64" + fi +fi + filter="${1:-}" tests=() for path in "$PROJECT_DIR"/tests/test_*.gd; do @@ -97,8 +156,13 @@ if [[ "$IN_PLACE" != "1" ]]; then -cf - . 2>/dev/null | tar -C "$run_dir" -xf - fi -proot-distro login ubuntu -- bash "$run_dir/tests/_run_inside.sh" \ - "$run_dir" "$JOBS" "$VERBOSE" "${tests[@]}" +if [[ "$PLATFORM" == "wsl" ]]; then + bash "$run_dir/tests/_run_inside.sh" \ + "$GODOT_BIN" "$run_dir" "$JOBS" "$VERBOSE" "${tests[@]}" +else + proot-distro login ubuntu -- bash "$run_dir/tests/_run_inside.sh" \ + "$GODOT_BIN" "$run_dir" "$JOBS" "$VERBOSE" "${tests[@]}" +fi status=$? echo "in $(( $(date +%s) - start ))s with JOBS=$JOBS"