On Windows, stop() with no live process of its own looked up the first "dragonxd.exe" by name and TerminateProcess'd it after the grace period: an old node still shutting down (how an in-progress shutdown was killed), a node on another datadir, or the user's main node. It was reached on a lock-gated close, for an external node, after the tracked process had exited, and from ~EmbeddedDaemon's stop(3000). - Windows stop(): drop the by-name branch; with no tracked process it logs and returns. findProcessByName is detection only. - ~EmbeddedDaemon no longer calls stop(); it stops watching and releases its handles. stop() returns whether the process exited on its own. - SeedWalletCreator never sent its isolated node an RPC stop, so on Windows that node was always force-killed holding the wallet.dat it had just created, and the stack object's destructor could then kill the main node by name. It now stops the node over the node's own RPC on every path. A node that still had to be force-killed fails the creation (seed wiped; nothing has been swept into it yet), so an uncleanly closed wallet is never adopted. - Reinstall waited only for the RPC port, which closes long before the process exits (locked exe on Windows, held datadir lock everywhere). It now waits for the process to exit and otherwise aborts without touching the binaries or starting a second node. - Linux isDaemonProcessRunning also matches /proc/<pid>/exe, so a node whose main thread is already renamed "hush-shutoff" (still holding the datadir lock) counts as running. Tests: a Windows real-process test (a fake dragonxd.exe on an isolated datadir survives stop() and the destructor), a POSIX test that the destructor leaves the process running, and the pure identity rule. scripts/test-windows.sh passes the stub path to the Windows suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
3.2 KiB
Bash
Executable File
80 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Cross-compile the test suite (ObsidianDragonTests.exe) and the fake daemon (tools/fake_dragonxd)
|
|
# for Windows with mingw-w64, then run the tests: directly under WSL (Windows interop), else with
|
|
# wine if installed, else just build.
|
|
#
|
|
# scripts/test-windows.sh [--no-run] [--jobs N]
|
|
#
|
|
# Uses its own build/windows-tests tree (full-node config), so build/windows is left alone. Needs
|
|
# the mingw toolchain file that ./build.sh --win-release writes to build/windows/. Dependency sources
|
|
# already fetched into build/windows/_deps are reused, so nothing is downloaded twice.
|
|
set -euo pipefail
|
|
|
|
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
bd="$repo/build/windows-tests"
|
|
toolchain="$repo/build/windows/mingw-toolchain.cmake"
|
|
deps="$repo/build/windows/_deps"
|
|
run=true
|
|
jobs=8 # each test TU is large; several parallel mingw builds can exhaust RAM
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--no-run) run=false; shift ;;
|
|
--jobs) jobs="$2"; shift 2 ;;
|
|
-h|--help) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
|
*) echo "unknown option: $1" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
if [[ ! -f "$toolchain" ]]; then
|
|
echo "missing $toolchain; run ./build.sh --win-release once to create the mingw toolchain" >&2
|
|
exit 1
|
|
fi
|
|
|
|
src_args=()
|
|
for dep in sdl3 json tomlplusplus sqlite3 curl libwebp; do
|
|
if [[ -d "$deps/$dep-src" ]]; then
|
|
src_args+=("-DFETCHCONTENT_SOURCE_DIR_${dep^^}=$deps/$dep-src")
|
|
fi
|
|
done
|
|
|
|
cmake -S "$repo" -B "$bd" \
|
|
-DCMAKE_TOOLCHAIN_FILE="$toolchain" \
|
|
-DCMAKE_BUILD_TYPE=Release \
|
|
-DDRAGONX_USE_SYSTEM_SDL3=OFF \
|
|
-DDRAGONX_BUILD_LITE=OFF \
|
|
-DDRAGONX_ENABLE_LITE_BACKEND=OFF \
|
|
-DBUILD_TESTING=ON \
|
|
-DDRAGONX_BUILD_FAKE_DAEMON=ON \
|
|
"${src_args[@]}"
|
|
cmake --build "$bd" --target ObsidianDragonTests fake_dragonxd -j "$jobs"
|
|
|
|
exe="$bd/bin/ObsidianDragonTests.exe"
|
|
echo "built: $exe"
|
|
echo "built: $bd/fake_dragonxd/dragonxd.exe"
|
|
$run || exit 0
|
|
|
|
# The fixture path compiled into the binary is a Linux path; hand the Windows process its own.
|
|
# APPDATA/LOCALAPPDATA point at a sandbox so anything resolving them from the environment stays out
|
|
# of the real profile. (SHGetFolderPath-based paths ignore the environment; the tests that would
|
|
# write through those are compiled out on Windows.)
|
|
export DRAGONX_TEST_FIXTURE_DIR="$repo/tests/fixtures"
|
|
sandbox="$bd/test-appdata"
|
|
mkdir -p "$sandbox"
|
|
export APPDATA="$sandbox" LOCALAPPDATA="$sandbox"
|
|
# The fake dragonxd.exe plays a "foreign" node (isolated datadir/port) for the never-kill-by-name test.
|
|
export DRAGONX_TEST_FAKE_DAEMON="$bd/fake_dragonxd/dragonxd.exe"
|
|
if grep -qi microsoft /proc/version 2>/dev/null && [[ -e /proc/sys/fs/binfmt_misc/WSLInterop ]]; then
|
|
# /p: translate each to a Windows path for the Windows process
|
|
export WSLENV="${WSLENV:+$WSLENV:}DRAGONX_TEST_FIXTURE_DIR/p:APPDATA/p:LOCALAPPDATA/p:DRAGONX_TEST_FAKE_DAEMON/p"
|
|
cd "$bd/bin"
|
|
exec "$exe"
|
|
elif command -v wine >/dev/null 2>&1; then
|
|
export DRAGONX_TEST_FIXTURE_DIR="$(winepath -w "$DRAGONX_TEST_FIXTURE_DIR")"
|
|
export DRAGONX_TEST_FAKE_DAEMON="$(winepath -w "$DRAGONX_TEST_FAKE_DAEMON")"
|
|
cd "$bd/bin"
|
|
exec wine "$exe"
|
|
else
|
|
echo "not on WSL and no wine: copy $exe (and tests/fixtures) to Windows to run it" >&2
|
|
fi
|