Stop a failed controller-DB fetch from destroying the tracked fallback

Closes Defects -> Known and still open item 12, both halves.

include/akgl/SDL_GameControllerDB.h is tracked on purpose: it is the offline
fallback that keeps the library buildable when upstream is unreachable. The
script that writes it had no set -e and never checked curl, so a failed fetch
wrote AKGL_SDL_GAMECONTROLLER_DB_LEN 0 and an empty array over the good copy
and exited 0. The safety net destroyed itself, and because CMake re-ran the
generator on every build, an offline build was enough to do it.

The script now runs under set -euo pipefail, fetches into a temporary
directory, and moves the result into place only after checking curl's exit
status (with --fail, so an HTTP error is a status rather than an error page in
the body), a plausible minimum mapping count, and that no mapping carries a
quote or backslash that would break the C string literal it becomes. Any of
those failing leaves the tracked header exactly as it was and exits non-zero.

Verified against all five failure modes -- unresolvable host, 404, truncated
response, empty-but-successful response (the original failure exactly), and a
response containing a quote. Each refuses, and the header's checksum is
unchanged after every one. AKGL_CONTROLLERDB_URL and
AKGL_CONTROLLERDB_MIN_LINES are environment-overridable, which is how.

The build no longer runs it at all. The add_custom_command declared a relative
OUTPUT, which CMake resolves against the binary directory while the script
writes to the source directory, so the declared output never appeared and every
build re-ran the command -- needing network access and leaving the tree dirty.
It is an explicit `controllerdb` target now, and the header is no longer listed
as a library source, which is all it was there for.

The empty-initializer problem goes with it: an empty array initializer is a
constraint violation in ISO C that compiles only as a GCC extension, and the
minimum-count check guarantees at least one entry.

Also here, because it rested on a premise that turned out to be false: the
character suite is no longer excluded from CI's coverage and memory-check jobs,
nor from the mutation harness by default. It was excluded on the grounds that
it failed deliberately. It did not -- it was reporting success while running
one of its four tests.

25/25 pass, memcheck clean, shellcheck clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 06:54:31 -04:00
parent 913834a3af
commit d9f0187ecf
6 changed files with 184 additions and 74 deletions

View File

@@ -42,7 +42,6 @@ jobs:
export LD_LIBRARY_PATH="$PWD/build:$PWD/build/deps/SDL:$PWD/build/deps/SDL_image:$PWD/build/deps/SDL_mixer:$PWD/build/deps/SDL_ttf"
ctest \
--test-dir build \
-E '^character$' \
--output-on-failure \
--output-junit "$(pwd)/ctest-junit.xml"
- name: Publish test results
@@ -161,11 +160,12 @@ jobs:
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build --parallel
# The character suite is excluded here for the same reason as in the
# coverage job: it fails deliberately, and a failing suite would be
# reported as "valgrind found nothing but ctest exited non-zero".
# Every suite, character included. It was excluded from both this job
# and the coverage job on the grounds that it failed deliberately -- it
# did not. It was reporting success while running one of its four tests;
# see TODO.md, "Test suites that could not fail".
- name: Memory check
run: scripts/memcheck.sh -E '^character$'
run: scripts/memcheck.sh
- name: Upload valgrind logs
if: always()
uses: actions/upload-artifact@v4