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:
41
AGENTS.md
41
AGENTS.md
@@ -19,26 +19,28 @@ Working rules:
|
||||
|
||||
- **Never hand-edit it.** It is machine-written; changes belong in
|
||||
`mkcontrollermappings.sh`.
|
||||
- **Do not commit incidental regeneration churn.** The build regenerates the
|
||||
header on every run, and the script stamps `$(date)` into a comment, so an
|
||||
ordinary build leaves the file modified with nothing of substance changed.
|
||||
Revert that before committing: `git checkout -- include/akgl/SDL_GameControllerDB.h`.
|
||||
- **An ordinary build does not touch it.** Regeneration is
|
||||
`cmake --build build --target controllerdb`, and nothing else runs the script.
|
||||
Until 0.5.0 an `add_custom_command` re-ran it on every build, which made every
|
||||
build need network access and left the file modified with a fresh `$(date)`
|
||||
stamp and nothing of substance changed.
|
||||
- **Update it as a deliberate, standalone commit** when you actually want newer
|
||||
mappings, so the diff is reviewable and bisectable.
|
||||
- **Never commit a copy with `AKGL_SDL_GAMECONTROLLER_DB_LEN 0`.** That is the
|
||||
signature of a failed fetch, not an empty upstream — see the defect note
|
||||
below. Check the constant before staging this file.
|
||||
- **A failed fetch cannot reach this file.** The script assembles the header in
|
||||
a temporary directory and moves it into place only after checking curl's exit
|
||||
status, a plausible minimum mapping count, and that no mapping contains a
|
||||
character that would break a C string literal. Any of those failing leaves the
|
||||
tracked copy untouched and exits non-zero.
|
||||
- Formatting tooling ignores it: `scripts/reindent.sh` and the pre-commit hook
|
||||
both skip it.
|
||||
|
||||
> **Known defect (tracked in `TODO.md`).** The safety net is currently
|
||||
> self-defeating. `mkcontrollermappings.sh` has no `set -e` and does not check
|
||||
> `curl`'s exit status, so when the fetch fails it writes a header with
|
||||
> `AKGL_SDL_GAMECONTROLLER_DB_LEN 0` and an empty array **over the good tracked
|
||||
> copy, and exits 0**. Because CMake re-runs the generator on every build, an
|
||||
> offline build silently destroys the very fallback the file exists to provide.
|
||||
> Until that is fixed, treat a dirty `SDL_GameControllerDB.h` after a build as
|
||||
> suspect and check the length constant before committing it.
|
||||
> **Fixed in 0.5.0.** The safety net used to be self-defeating:
|
||||
> `mkcontrollermappings.sh` 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 tracked copy, and exited 0** — and because CMake re-ran the
|
||||
> generator on every build, an offline build silently destroyed the very
|
||||
> fallback the file exists to provide. Both halves are closed: the script
|
||||
> refuses rather than overwrites, and a build does not run it.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
@@ -75,7 +77,7 @@ because the perf suites scale themselves down under valgrind. Suppressions for
|
||||
third-party findings live in `scripts/valgrind.supp`; add one only when the
|
||||
finding genuinely cannot be fixed from this repository.
|
||||
|
||||
Run mutation testing with `cmake --build build --target mutation`. For a quick smoke run, use `scripts/mutation_test.py --target src/tilemap.c --max-mutants 10`; the harness mutates only a scratch copy and excludes the intentionally failing character test.
|
||||
Run mutation testing with `cmake --build build --target mutation`. For a quick smoke run, use `scripts/mutation_test.py --target src/tilemap.c --max-mutants 10`; the harness mutates only a scratch copy and runs every suite.
|
||||
|
||||
Generate HTML and Cobertura coverage reports with `cmake -S . -B build-coverage -DAKGL_COVERAGE=ON -DCMAKE_BUILD_TYPE=Debug`, then build and run CTest. Reports are written to `build-coverage/coverage/`; this mode requires `gcovr` and GCC or Clang.
|
||||
|
||||
@@ -91,8 +93,11 @@ because the others cannot do its work:
|
||||
| `memory_check` | RelWithDebInfo | `scripts/memcheck.sh`, every suite under valgrind. Gates: a definite leak, an invalid access or a branch on uninitialised memory fails the build. Keeps the valgrind logs as an artifact. |
|
||||
| `mutation_test` | Debug | One focused source file, so CI stays bounded. |
|
||||
|
||||
The `character` suite is excluded wherever a whole run is selected: it fails
|
||||
deliberately, and pinning current-but-wrong behavior is not what it is for.
|
||||
Every suite runs in every job. The `character` suite used to be excluded from
|
||||
the coverage and memory-check jobs on the grounds that it failed deliberately.
|
||||
It did not: it was reporting success while running one of its four tests, for
|
||||
two independent reasons, and both are fixed. See TODO.md, "Test suites that
|
||||
could not fail".
|
||||
|
||||
## Coding Style
|
||||
|
||||
|
||||
Reference in New Issue
Block a user